From dfa56fde1e5040ab15465d58a0d7c42439c8fbf0 Mon Sep 17 00:00:00 2001 From: songjianet <1778651752@qq.com> Date: Fri, 5 May 2023 20:20:05 +0900 Subject: [PATCH 01/28] [Feat][UI] Add component of dynamic form in this project. --- .../dynamic-form/dynamic-form-item.tsx | 165 ++++++++++++++++++ .../components/dynamic-form/use-form-field.ts | 32 ++++ .../dynamic-form/use-form-locales.ts | 26 +++ .../dynamic-form/use-form-request.ts | 39 +++++ .../dynamic-form/use-form-structure.ts | 25 +++ .../dynamic-form/use-form-validate.ts | 69 ++++++++ 6 files changed, 356 insertions(+) create mode 100644 seatunnel-ui/src/components/dynamic-form/dynamic-form-item.tsx create mode 100644 seatunnel-ui/src/components/dynamic-form/use-form-field.ts create mode 100644 seatunnel-ui/src/components/dynamic-form/use-form-locales.ts create mode 100644 seatunnel-ui/src/components/dynamic-form/use-form-request.ts create mode 100644 seatunnel-ui/src/components/dynamic-form/use-form-structure.ts create mode 100644 seatunnel-ui/src/components/dynamic-form/use-form-validate.ts diff --git a/seatunnel-ui/src/components/dynamic-form/dynamic-form-item.tsx b/seatunnel-ui/src/components/dynamic-form/dynamic-form-item.tsx new file mode 100644 index 000000000..e29727fb9 --- /dev/null +++ b/seatunnel-ui/src/components/dynamic-form/dynamic-form-item.tsx @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineComponent } from 'vue' +import { + NFormItemGi, + NGrid, + NInput, + NSelect, + NCheckboxGroup, + NSpace, + NCheckbox +} from 'naive-ui' +import { useI18n } from 'vue-i18n' +import type { PropType } from 'vue' +import type { SelectOption } from 'naive-ui' + +const props = { + formStructure: { + type: Object as PropType> + }, + model: { + type: Object as PropType + }, + name: { + type: String as PropType, + default: '' + }, + locales: { + type: Object as PropType + } +} + +const DynamicFormItem = defineComponent({ + name: 'DynamicFormItem', + props, + setup(props) { + const { t } = useI18n() + + if (props.locales) { + useI18n().mergeLocaleMessage('zh_CN', { + i18n: (props.locales as any).zh_CN + }) + useI18n().mergeLocaleMessage('en_US', { + i18n: (props.locales as any).en_US + }) + } + + const formatClass = (name: string, modelField: string) => { + return name.indexOf('[') >= 0 + ? name.split('[')[0].toLowerCase() + name.split('[')[1].split(']')[0] + : name.toLowerCase() + '-' + modelField.toLowerCase() + } + + const formItemDisabled = (field: string, value: Array) => { + return value.map((v) => field === v).indexOf(false) < 0 + } + + return { + t, + formatClass, + formItemDisabled + } + }, + render() { + return ( + + {(this.formStructure as Array).map((f) => { + return ( + (f.show + ? this.formItemDisabled( + (this.model as any)[f.show.field], + f.show.value + ) + : true) && ( + + {f.type === 'input' && ( + + )} + {f.type === 'select' && + (f.show + ? this.formItemDisabled( + (this.model as any)[f.show.field], + f.show.value + ) + : true) && ( + { + return { + label: this.t(o.label as string), + value: o.value + } + }) + } + /> + )} + {f.type === 'checkbox' && + (f.show + ? this.formItemDisabled( + (this.model as any)[f.show.field], + f.show.value + ) + : true) && ( + + + {f.options.map((o: any) => ( + + ))} + + + )} + + ) + ) + })} + + ) + } +}) + +export { DynamicFormItem } diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-field.ts b/seatunnel-ui/src/components/dynamic-form/use-form-field.ts new file mode 100644 index 000000000..3d6a0e972 --- /dev/null +++ b/seatunnel-ui/src/components/dynamic-form/use-form-field.ts @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function useFormField(forms: Array) { + const model: any = {} + + const setField = (value: string, type: string): null | string => { + return value ? value : type === 'select' ? null : '' + } + + forms.forEach((f: any) => { + if (!f.field) return + + model[f.field] = setField(f.defaultValue, f.type) + }) + + return model +} diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-locales.ts b/seatunnel-ui/src/components/dynamic-form/use-form-locales.ts new file mode 100644 index 000000000..90d8c7c07 --- /dev/null +++ b/seatunnel-ui/src/components/dynamic-form/use-form-locales.ts @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useI18n } from 'vue-i18n' + +export function useFormLocales(locales: { + zh_CN: object + en_US: object +}): void { + useI18n().mergeLocaleMessage('zh_CN', { i18n: locales.zh_CN }) + useI18n().mergeLocaleMessage('en_US', { i18n: locales.en_US }) +} diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-request.ts b/seatunnel-ui/src/components/dynamic-form/use-form-request.ts new file mode 100644 index 000000000..6ba882e93 --- /dev/null +++ b/seatunnel-ui/src/components/dynamic-form/use-form-request.ts @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { axios } from '@/service/service' + +const reqFunction = (url: string, method: string) => { + return axios({ + url, + method + } as any) +} + +export function useFormRequest(apis: any, forms: Array): Array { + forms.map(f => { + if (f.api) { + reqFunction(apis[f.api].url, apis[f.api].method).then((res: any) => { + f.options = res.map((r: any) => { + return { label: r.label, value: r.value } + }) + }) + } + }) + + return forms +} \ No newline at end of file diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-structure.ts b/seatunnel-ui/src/components/dynamic-form/use-form-structure.ts new file mode 100644 index 000000000..34166ac65 --- /dev/null +++ b/seatunnel-ui/src/components/dynamic-form/use-form-structure.ts @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function useFormStructure(forms: Array) { + return forms.map((f: any) => { + delete f.validate + delete f.api + + return f + }) +} \ No newline at end of file diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-validate.ts b/seatunnel-ui/src/components/dynamic-form/use-form-validate.ts new file mode 100644 index 000000000..86be15ae4 --- /dev/null +++ b/seatunnel-ui/src/components/dynamic-form/use-form-validate.ts @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { FormItemRule } from 'naive-ui' + +export function useFormValidate(forms: Array, model: any, t: any) { + const validate: any = {} + + const setValidate = (validate: any, field: string): object => { + const data: any = { + required: validate.required, + trigger: validate.trigger + } + + if (validate.type === 'non-empty') { + data['validator'] = (rule: FormItemRule, value: string) => { + if (!model[field]) { + return Error(t(validate.message)) + } + } + } else if (validate.type === 'union-non-empty') { + const fields = validate.fields.splice( + validate.fields.findIndex((f: any) => f !== field), + 1 + ) + data['validator'] = (rule: FormItemRule, value: string) => { + const fieldsValidate = fields.map((f: string) => !!model[f]) + if (!value && fieldsValidate.includes(true)) { + return Error(t(validate.message)) + } + } + } else if (validate.type === 'mutually-exclusive') { + const fields = validate.fields.splice( + validate.fields.findIndex((f: any) => f !== field), + 1 + ) + data['validator'] = (rule: FormItemRule, value: string) => { + const fieldsValidate = fields.map((f: string) => !!model[f]) + if (value && fieldsValidate.indexOf(false) < 0) { + return Error(t(validate.message)) + } + } + } + + return data + } + + forms.forEach((f: any) => { + if (!f.validate || Object.keys(f.validate).length <= 0) return + + validate[f.field] = setValidate(f.validate, f.field) + }) + + return validate +} From 0ad077fb6dd43d150d7ada69ad0dd435187244d8 Mon Sep 17 00:00:00 2001 From: songjianet <1778651752@qq.com> Date: Mon, 8 May 2023 22:01:37 +0800 Subject: [PATCH 02/28] [Feat][UI] Add chinese locale in the project. --- seatunnel-ui/src/App.tsx | 19 +++++--- .../layouts/dashboard/header/user/index.tsx | 39 +++++++++++++--- .../header/user/use-user-dropdown.ts | 42 +---------------- seatunnel-ui/src/locales/en_US/data-pipes.ts | 8 +--- seatunnel-ui/src/locales/en_US/jobs.ts | 2 +- seatunnel-ui/src/locales/en_US/menu.ts | 1 - seatunnel-ui/src/locales/en_US/setting.ts | 1 + seatunnel-ui/src/locales/en_US/user-manage.ts | 4 +- seatunnel-ui/src/locales/zh_CN/data-pipes.ts | 45 +++++++++++++++++++ seatunnel-ui/src/locales/zh_CN/index.ts | 22 ++++++++- .../locale/index.ts => locales/zh_CN/jobs.ts} | 33 ++++++-------- .../locale/types.ts => locales/zh_CN/log.ts} | 9 ++-- seatunnel-ui/src/locales/zh_CN/login.ts | 25 +++++++++++ seatunnel-ui/src/locales/zh_CN/menu.ts | 26 +++++++++++ seatunnel-ui/src/locales/zh_CN/modal.ts | 21 +++++++++ seatunnel-ui/src/locales/zh_CN/setting.ts | 35 +++++++++++++++ seatunnel-ui/src/locales/zh_CN/tasks.ts | 44 ++++++++++++++++++ seatunnel-ui/src/locales/zh_CN/user-manage.ts | 39 ++++++++++++++++ seatunnel-ui/src/store/setting/index.ts | 11 ++++- seatunnel-ui/src/store/setting/types.ts | 5 ++- seatunnel-ui/src/views/jobs/list/use-table.ts | 2 +- seatunnel-ui/src/views/setting/index.tsx | 44 +++++++++--------- .../list/components/form-modal.tsx | 2 +- 23 files changed, 361 insertions(+), 118 deletions(-) create mode 100644 seatunnel-ui/src/locales/zh_CN/data-pipes.ts rename seatunnel-ui/src/{store/locale/index.ts => locales/zh_CN/jobs.ts} (67%) rename seatunnel-ui/src/{store/locale/types.ts => locales/zh_CN/log.ts} (87%) create mode 100644 seatunnel-ui/src/locales/zh_CN/login.ts create mode 100644 seatunnel-ui/src/locales/zh_CN/menu.ts create mode 100644 seatunnel-ui/src/locales/zh_CN/modal.ts create mode 100644 seatunnel-ui/src/locales/zh_CN/setting.ts create mode 100644 seatunnel-ui/src/locales/zh_CN/tasks.ts create mode 100644 seatunnel-ui/src/locales/zh_CN/user-manage.ts diff --git a/seatunnel-ui/src/App.tsx b/seatunnel-ui/src/App.tsx index 96635f885..6ec016b37 100644 --- a/seatunnel-ui/src/App.tsx +++ b/seatunnel-ui/src/App.tsx @@ -26,8 +26,8 @@ import { enUS } from 'naive-ui' import { useThemeStore } from '@/store/theme' -import { useLocalesStore } from '@/store/locale' import { useSettingStore } from '@/store/setting' +import { useI18n } from 'vue-i18n' import themeList from '@/themes' import type { GlobalThemeOverrides } from 'naive-ui' import type { Ref } from 'vue' @@ -35,25 +35,30 @@ import type { Ref } from 'vue' const App = defineComponent({ setup() { const themeStore = useThemeStore() + const settingStore = useSettingStore() const currentTheme = computed(() => themeStore.darkTheme ? darkTheme : undefined ) - const localesStore = useLocalesStore() const themeOverrides: Ref = ref( themeList[currentTheme.value ? 'dark' : 'light'] ) + if (settingStore.getLocales) { + const { locale } = useI18n() + locale.value = settingStore.getLocales + } + watch( - () => useSettingStore().getFilletValue, + () => settingStore.getFilletValue, () => { ;(themeOverrides.value.common as any).borderRadius = - useSettingStore().getFilletValue + 'px' + settingStore.getFilletValue + 'px' } ) return { + settingStore, currentTheme, - localesStore, themeOverrides } }, @@ -63,9 +68,9 @@ const App = defineComponent({ theme={this.currentTheme} theme-overrides={this.themeOverrides} date-locale={ - String(this.localesStore.getLocales) === 'zh_CN' ? dateZhCN : dateEnUS + this.settingStore.getLocales === 'zh_CN' ? dateZhCN : dateEnUS } - locale={String(this.localesStore.getLocales) === 'zh_CN' ? zhCN : enUS} + locale={this.settingStore.getLocales === 'zh_CN' ? zhCN : enUS} > diff --git a/seatunnel-ui/src/layouts/dashboard/header/user/index.tsx b/seatunnel-ui/src/layouts/dashboard/header/user/index.tsx index b5d1da252..26f9e104c 100644 --- a/seatunnel-ui/src/layouts/dashboard/header/user/index.tsx +++ b/seatunnel-ui/src/layouts/dashboard/header/user/index.tsx @@ -15,22 +15,33 @@ * limitations under the License. */ -import { defineComponent, toRefs } from 'vue' +import { Component, defineComponent, h } from 'vue' import { NSpace, NDropdown, NIcon, NButton } from 'naive-ui' import { useUserDropdown } from './use-user-dropdown' import { useUserStore } from '@/store/user' -import { DownOutlined, UserOutlined } from '@vicons/antd' +import { DownOutlined, LogoutOutlined, QuestionCircleOutlined, SettingOutlined, UserOutlined } from '@vicons/antd' +import { useI18n } from 'vue-i18n' import type { UserDetail } from '@/service/user/types' const User = defineComponent({ setup() { - const { state, handleSelect } = useUserDropdown() + const { handleSelect } = useUserDropdown() + const { t } = useI18n() const userDetail = useUserStore() + const renderIcon = (icon: Component) => { + return () => { + return h(NIcon, null, { + default: () => h(icon) + }) + } + } + return { - ...toRefs(state), + t, handleSelect, - userDetail + userDetail, + renderIcon } }, render() { @@ -43,7 +54,23 @@ const User = defineComponent({ > diff --git a/seatunnel-ui/src/layouts/dashboard/header/user/use-user-dropdown.ts b/seatunnel-ui/src/layouts/dashboard/header/user/use-user-dropdown.ts index f1bafe793..7611ba352 100644 --- a/seatunnel-ui/src/layouts/dashboard/header/user/use-user-dropdown.ts +++ b/seatunnel-ui/src/layouts/dashboard/header/user/use-user-dropdown.ts @@ -15,55 +15,15 @@ * limitations under the License. */ -import { reactive, h } from 'vue' -import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' -import { NIcon } from 'naive-ui' import { userLogout } from '@/service/user' import { useUserStore } from '@/store/user' -import { - LogoutOutlined, - QuestionCircleOutlined, - SettingOutlined -} from '@vicons/antd' import type { Router } from 'vue-router' -import type { Component } from 'vue' export function useUserDropdown() { const router: Router = useRouter() - const { t } = useI18n() const userStore = useUserStore() - const renderIcon = (icon: Component) => { - return () => { - return h(NIcon, null, { - default: () => h(icon) - }) - } - } - - const dropdownOptions = [ - { - key: 'help', - label: t('menu.help'), - icon: renderIcon(QuestionCircleOutlined) - }, - { - key: 'setting', - label: t('menu.setting'), - icon: renderIcon(SettingOutlined) - }, - { - key: 'logout', - label: t('menu.logout'), - icon: renderIcon(LogoutOutlined) - } - ] - - const state = reactive({ - dropdownOptions - }) - const handleSelect = (key: string) => { if (key === 'help') { window.open('http://seatunnel.incubator.apache.org/versions/') @@ -77,5 +37,5 @@ export function useUserDropdown() { } } - return { state, handleSelect } + return { handleSelect } } diff --git a/seatunnel-ui/src/locales/en_US/data-pipes.ts b/seatunnel-ui/src/locales/en_US/data-pipes.ts index af3817706..c8d549f6b 100644 --- a/seatunnel-ui/src/locales/en_US/data-pipes.ts +++ b/seatunnel-ui/src/locales/en_US/data-pipes.ts @@ -24,10 +24,6 @@ export default { create_time: 'Create Time', update_time: 'Update Time', operation: 'Operation', - succeed: 'Succeed', - running: 'Running', - failed: 'Failed', - killed: 'Killed', un_start: 'Un Start', execute: 'Execute', edit: 'Edit', @@ -43,7 +39,7 @@ export default { value: 'Value', name_tips: 'Required fields,number, letter case, 100 characters', data_pipes_delete_tips: - 'Whether to delete the data Pipe,It cannot be restored after being deleted.', - data_pipes_publish_tips: 'Whether to Publish the data pipe.', + 'Whether to delete the data Pipe,It cannot be restored after being deleted', + data_pipes_publish_tips: 'Whether to Publish the data pipe', model_validate_tips: 'Required Fields' } diff --git a/seatunnel-ui/src/locales/en_US/jobs.ts b/seatunnel-ui/src/locales/en_US/jobs.ts index 99de8afb6..f7f53f4aa 100644 --- a/seatunnel-ui/src/locales/en_US/jobs.ts +++ b/seatunnel-ui/src/locales/en_US/jobs.ts @@ -22,7 +22,7 @@ export default { search: 'Search', data_pipe_name: 'Data Pipe Name', plan: 'Plan', - create_date: 'Create Date', + create_time: 'Create Time', publish: 'Publish', operation: 'Operation', execute: 'Execute', diff --git a/seatunnel-ui/src/locales/en_US/menu.ts b/seatunnel-ui/src/locales/en_US/menu.ts index 37fd071ba..c55668705 100644 --- a/seatunnel-ui/src/locales/en_US/menu.ts +++ b/seatunnel-ui/src/locales/en_US/menu.ts @@ -18,7 +18,6 @@ export default { data_pipes: 'Data Pipes', jobs: 'Jobs', - manage: 'Manage', user_manage: 'User Manage', help: 'Help', setting: 'Setting', diff --git a/seatunnel-ui/src/locales/en_US/setting.ts b/seatunnel-ui/src/locales/en_US/setting.ts index 9ce36be74..fd2701465 100644 --- a/seatunnel-ui/src/locales/en_US/setting.ts +++ b/seatunnel-ui/src/locales/en_US/setting.ts @@ -26,6 +26,7 @@ export default { theme_setting: 'Theme Setting', theme: 'Theme', english: 'English', + chinese: 'Chinese', light: 'Light', model: 'Model', hue: 'Hue', diff --git a/seatunnel-ui/src/locales/en_US/user-manage.ts b/seatunnel-ui/src/locales/en_US/user-manage.ts index eaba9da01..f689e8edd 100644 --- a/seatunnel-ui/src/locales/en_US/user-manage.ts +++ b/seatunnel-ui/src/locales/en_US/user-manage.ts @@ -20,7 +20,7 @@ export default { user_manage: 'User Manage', create: 'Create', username: 'Username', - status: 'Status', + state: 'State', create_time: 'Create Time', update_time: 'Update Time', operation: 'Operation', @@ -35,5 +35,5 @@ export default { username_tips: 'Required fields, number, letter case, 50 characters', password_tips: 'Required fields, number, letter case, 6 characters', user_delete_tips: - 'Whether to delete the user? It cannot be restored after being deleted.' + 'Whether to delete the user? It cannot be restored after being deleted' } diff --git a/seatunnel-ui/src/locales/zh_CN/data-pipes.ts b/seatunnel-ui/src/locales/zh_CN/data-pipes.ts new file mode 100644 index 000000000..70b0de793 --- /dev/null +++ b/seatunnel-ui/src/locales/zh_CN/data-pipes.ts @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + id: 'Id', + data_pipes: '数据管道', + create: '创建', + name: '名称', + state: '状态', + create_time: '创建时间', + update_time: '更新时间e', + operation: '操作', + un_start: '取消开始', + execute: '执行', + edit: '编辑', + publish: '发布', + delete: '删除', + save: '保存', + cancel: '取消', + script: '脚本', + kill: '终止', + stop: '停止', + add: '添加', + key: '键', + value: '值', + name_tips: '必填字段,数字,字母大小写,100个字符', + data_pipes_delete_tips: + '是否删除数据管道,删除后无法恢复', + data_pipes_publish_tips: '是否发布数据管道', + model_validate_tips: '必填字段' +} diff --git a/seatunnel-ui/src/locales/zh_CN/index.ts b/seatunnel-ui/src/locales/zh_CN/index.ts index 39626a390..04351711c 100644 --- a/seatunnel-ui/src/locales/zh_CN/index.ts +++ b/seatunnel-ui/src/locales/zh_CN/index.ts @@ -15,4 +15,24 @@ * limitations under the License. */ -export default {} +import login from '@/locales/zh_CN/login' +import menu from '@/locales/zh_CN/menu' +import modal from '@/locales/zh_CN/modal' +import user_manage from '@/locales/zh_CN/user-manage' +import data_pipes from '@/locales/zh_CN/data-pipes' +import log from '@/locales/zh_CN/log' +import jobs from '@/locales/zh_CN/jobs' +import tasks from '@/locales/zh_CN/tasks' +import setting from '@/locales/zh_CN/setting' + +export default { + login, + menu, + modal, + user_manage, + data_pipes, + log, + jobs, + tasks, + setting +} diff --git a/seatunnel-ui/src/store/locale/index.ts b/seatunnel-ui/src/locales/zh_CN/jobs.ts similarity index 67% rename from seatunnel-ui/src/store/locale/index.ts rename to seatunnel-ui/src/locales/zh_CN/jobs.ts index 1b31f7a9b..94cd79f05 100644 --- a/seatunnel-ui/src/store/locale/index.ts +++ b/seatunnel-ui/src/locales/zh_CN/jobs.ts @@ -15,23 +15,16 @@ * limitations under the License. */ -import { defineStore } from 'pinia' -import { LocalesStore, Locales } from './types' - -export const useLocalesStore = defineStore({ - id: 'locales', - state: (): LocalesStore => ({ - locales: 'en_US' - }), - persist: true, - getters: { - getLocales(): Locales { - return this.locales - } - }, - actions: { - setLocales(lang: Locales): void { - this.locales = lang - } - } -}) +export default { + script_id: '脚本 Id', + job_id: '工作 Id', + jobs: '工作', + search: '搜索', + data_pipe_name: '数据管道名称', + plan: '计划', + create_time: '创建时间', + publish: '发布', + operation: '操作', + execute: '执行', + recycle: '回收' +} diff --git a/seatunnel-ui/src/store/locale/types.ts b/seatunnel-ui/src/locales/zh_CN/log.ts similarity index 87% rename from seatunnel-ui/src/store/locale/types.ts rename to seatunnel-ui/src/locales/zh_CN/log.ts index 03625ade5..f80cc8335 100644 --- a/seatunnel-ui/src/store/locale/types.ts +++ b/seatunnel-ui/src/locales/zh_CN/log.ts @@ -15,10 +15,7 @@ * limitations under the License. */ -type Locales = 'zh_CN' | 'en_US' - -interface LocalesStore { - locales: Locales +export default { + please_select_log: '请选择日志', + search: '搜索' } - -export { LocalesStore, Locales } diff --git a/seatunnel-ui/src/locales/zh_CN/login.ts b/seatunnel-ui/src/locales/zh_CN/login.ts new file mode 100644 index 000000000..db12f1718 --- /dev/null +++ b/seatunnel-ui/src/locales/zh_CN/login.ts @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + login_to_sea_tunnel: '登录 SeaTunnel', + login: '登录', + username: '用户名', + password: '密码', + username_tips: '请输入用户名', + password_tips: '请输入密码' +} diff --git a/seatunnel-ui/src/locales/zh_CN/menu.ts b/seatunnel-ui/src/locales/zh_CN/menu.ts new file mode 100644 index 000000000..e689b51ea --- /dev/null +++ b/seatunnel-ui/src/locales/zh_CN/menu.ts @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + data_pipes: '数据管道', + jobs: '工作', + user_manage: '用户管理', + help: '帮助', + setting: '设置', + logout: '登出', + tasks: '任务' +} diff --git a/seatunnel-ui/src/locales/zh_CN/modal.ts b/seatunnel-ui/src/locales/zh_CN/modal.ts new file mode 100644 index 000000000..155a855b7 --- /dev/null +++ b/seatunnel-ui/src/locales/zh_CN/modal.ts @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + cancel: '取消', + confirm: '确认' +} diff --git a/seatunnel-ui/src/locales/zh_CN/setting.ts b/seatunnel-ui/src/locales/zh_CN/setting.ts new file mode 100644 index 000000000..6c94c4b94 --- /dev/null +++ b/seatunnel-ui/src/locales/zh_CN/setting.ts @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + table_setting: '表格设置', + sequence_column: '序号列', + data_unique_value: '数据唯一值', + language_setting: '语言设置', + request_setting: '请求设置', + request_time: '请求时间', + language: '语言', + theme_setting: '主题设置', + theme: '主题', + english: '英文', + chinese: '中文', + light: '浅色', + model: '模式', + hue: '色调', + purple: '紫色', + fillet: '圆角' +} diff --git a/seatunnel-ui/src/locales/zh_CN/tasks.ts b/seatunnel-ui/src/locales/zh_CN/tasks.ts new file mode 100644 index 000000000..031eb94d0 --- /dev/null +++ b/seatunnel-ui/src/locales/zh_CN/tasks.ts @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + instance_id: '实例 Id', + job_id: '工作 Id', + tasks: '任务', + search: '搜索', + success: '成功', + fail: '失败', + stop: '停止', + running: '运行中', + unknown: '未知', + unpublished: '未发布', + published: '发布', + task_name: '任务名称', + state: '状态', + run_frequency: '运行频率', + once: '一次', + crontab: '定时', + start_time: '开始时间', + end_time: '结束时间', + last_total_bytes: '最后总字节数', + last_total_records: '最后总记录', + rerun: '重新运行', + kill: '终止', + operation: '操作', + view_log: '查看日志', + log: '日志' +} diff --git a/seatunnel-ui/src/locales/zh_CN/user-manage.ts b/seatunnel-ui/src/locales/zh_CN/user-manage.ts new file mode 100644 index 000000000..46a60c6f4 --- /dev/null +++ b/seatunnel-ui/src/locales/zh_CN/user-manage.ts @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default { + id: 'Id', + user_manage: '用户管理', + create: '创建', + username: '用户名', + state: '状态', + create_time: '创建时间', + update_time: '更新时间', + operation: '操作', + enable: '启用', + disable: '禁用', + edit: '编辑', + delete: '删除', + active: '激活', + inactive: '未激活', + password: '密码', + model_validate_tips: '必填字段', + username_tips: '必填字段,数字,字母大小写,50 个字符', + password_tips: '必填字段,数字,字母大小写,6 个字符', + user_delete_tips: + '是否删除用户? 删除后无法恢复' +} diff --git a/seatunnel-ui/src/store/setting/index.ts b/seatunnel-ui/src/store/setting/index.ts index 561e0b910..b22e9f8d6 100644 --- a/seatunnel-ui/src/store/setting/index.ts +++ b/seatunnel-ui/src/store/setting/index.ts @@ -16,7 +16,7 @@ */ import { defineStore } from 'pinia' -import { SettingStore } from './types' +import type { SettingStore, Locales } from './types' export const useSettingStore = defineStore({ id: 'setting', @@ -24,7 +24,8 @@ export const useSettingStore = defineStore({ sequenceColumn: false, dataUniqueValue: false, fillet: 15, - requestTime: 6000 + requestTime: 6000, + locales: 'en_US' }), persist: true, getters: { @@ -39,6 +40,9 @@ export const useSettingStore = defineStore({ }, getRequestTimeValue(): number { return this.requestTime + }, + getLocales(): Locales { + return this.locales } }, actions: { @@ -53,6 +57,9 @@ export const useSettingStore = defineStore({ }, setRequestTimeValue(status: number): void { this.requestTime = status + }, + setLocales(lang: Locales): void { + this.locales = lang } } }) diff --git a/seatunnel-ui/src/store/setting/types.ts b/seatunnel-ui/src/store/setting/types.ts index ba5327f5c..3652c715f 100644 --- a/seatunnel-ui/src/store/setting/types.ts +++ b/seatunnel-ui/src/store/setting/types.ts @@ -15,11 +15,14 @@ * limitations under the License. */ +type Locales = 'zh_CN' | 'en_US' + interface SettingStore { sequenceColumn: boolean dataUniqueValue: boolean fillet: number requestTime: number + locales: Locales } -export { SettingStore } +export { SettingStore, Locales } diff --git a/seatunnel-ui/src/views/jobs/list/use-table.ts b/seatunnel-ui/src/views/jobs/list/use-table.ts index 1b910d5b3..e7ac20707 100644 --- a/seatunnel-ui/src/views/jobs/list/use-table.ts +++ b/seatunnel-ui/src/views/jobs/list/use-table.ts @@ -51,7 +51,7 @@ export function useTable() { key: 'jobPlan' }, { - title: t('jobs.create_date'), + title: t('jobs.create_time'), key: 'createTime' }, { diff --git a/seatunnel-ui/src/views/setting/index.tsx b/seatunnel-ui/src/views/setting/index.tsx index fc7e94b17..04f86c8fe 100644 --- a/seatunnel-ui/src/views/setting/index.tsx +++ b/seatunnel-ui/src/views/setting/index.tsx @@ -22,21 +22,16 @@ import { useSettingStore } from '@/store/setting' const Setting = defineComponent({ name: 'Setting', - setup() { - const { t } = useI18n() - - return { - t - } - }, render() { + const { t, locale } = useI18n() + return ( - + - {this.t('setting.sequence_column')} + {t('setting.sequence_column')} { @@ -47,7 +42,7 @@ const Setting = defineComponent({ - {this.t('setting.data_unique_value')} + {t('setting.data_unique_value')} { @@ -58,28 +53,33 @@ const Setting = defineComponent({ - + - {this.t('setting.language')} + {t('setting.language')}
{ + locale.value = l + useSettingStore().setLocales(l) + }} />
- + - {this.t('setting.request_time')} + {t('setting.request_time')}
- + - {this.t('setting.model')} + {t('setting.model')}
@@ -116,12 +116,12 @@ const Setting = defineComponent({
- {this.t('setting.hue')} + {t('setting.hue')}
@@ -129,7 +129,7 @@ const Setting = defineComponent({
- {this.t('setting.fillet')} + {t('setting.fillet')}
- + {this.t('user_manage.enable')} {this.t('user_manage.disable')} From dc4704fb455080213f16b1022b100769c4c049f8 Mon Sep 17 00:00:00 2001 From: songjianet <1778651752@qq.com> Date: Tue, 9 May 2023 15:20:18 +0800 Subject: [PATCH 03/28] [Fix][UI] Fix the problem that the fillet is reset after refreshing. --- seatunnel-ui/src/App.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/seatunnel-ui/src/App.tsx b/seatunnel-ui/src/App.tsx index 6ec016b37..0a0069341 100644 --- a/seatunnel-ui/src/App.tsx +++ b/seatunnel-ui/src/App.tsx @@ -31,6 +31,7 @@ import { useI18n } from 'vue-i18n' import themeList from '@/themes' import type { GlobalThemeOverrides } from 'naive-ui' import type { Ref } from 'vue' +import type { CustomThemeCommonVars, ThemeCommonVars } from 'naive-ui/es/config-provider/src/interface' const App = defineComponent({ setup() { @@ -42,6 +43,12 @@ const App = defineComponent({ const themeOverrides: Ref = ref( themeList[currentTheme.value ? 'dark' : 'light'] ) + const setBorderRadius = (v: number) => { + (themeOverrides.value.common as Partial).borderRadius = + v + 'px' + } + + settingStore.getFilletValue && setBorderRadius(settingStore.getFilletValue) if (settingStore.getLocales) { const { locale } = useI18n() @@ -51,8 +58,7 @@ const App = defineComponent({ watch( () => settingStore.getFilletValue, () => { - ;(themeOverrides.value.common as any).borderRadius = - settingStore.getFilletValue + 'px' + setBorderRadius(settingStore.getFilletValue) } ) From 2ab3b956c0954a08c1c416a00f4af86d616525e0 Mon Sep 17 00:00:00 2001 From: songjianet <1778651752@qq.com> Date: Tue, 9 May 2023 17:41:03 +0800 Subject: [PATCH 04/28] [Feat][UI] Add 401 support. --- seatunnel-ui/src/service/service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/seatunnel-ui/src/service/service.ts b/seatunnel-ui/src/service/service.ts index d78f4f104..8897f28d0 100644 --- a/seatunnel-ui/src/service/service.ts +++ b/seatunnel-ui/src/service/service.ts @@ -22,6 +22,7 @@ import axios, { InternalAxiosRequestConfig } from 'axios' import utils from '@/utils' +import router from '@/router' import { useUserStore } from '@/store/user' import { useSettingStore } from '@/store/setting' import type { UserDetail } from '@/service/user/types' @@ -47,6 +48,11 @@ const baseRequestConfig: AxiosRequestConfig = { const service = axios.create(baseRequestConfig) const err = (err: AxiosError): Promise => { + const userStore = useUserStore() + if (err.response?.status === 401) { + userStore.setUserInfo({}) + router.push({ path: '/login' }) + } return Promise.reject(err) } From bc6d4af0df0b3a42dfd6563160d8920a5013e083 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 10 May 2023 16:19:22 +0800 Subject: [PATCH 05/28] [Sync] Sync main branch to add_canvas_job_define branch (#52) * [Doc&Web] Improve readme add how to start seatunnel web (#51) * Improve the readme * Make support deploy by installer package * [Feat][UI] Add component of dynamic form in this project. * [Feat][UI] Add chinese locale in the project. * [Fix][UI] Fix the problem that the fillet is reset after refreshing. --------- Co-authored-by: songjianet <1778651752@qq.com> --- .gitignore | 4 + README.md | 158 +++++++++++------- build.sh | 2 +- docs/images/application_config.png | Bin 0 -> 392200 bytes docs/images/ds_create_project.png | Bin 0 -> 202373 bytes docs/images/ds_create_tenant.png | Bin 0 -> 267768 bytes docs/images/ds_create_token.png | Bin 0 -> 268746 bytes docs/images/ds_create_user.png | Bin 0 -> 294395 bytes seatunnel-server/seatunnel-app/pom.xml | 87 +++++++--- .../src/main/assembly/seatunnel-web-ci.xml | 78 +++++++++ .../src/main/assembly/seatunnel-web.xml | 12 +- .../app/adapter/SeatunnelWebAdapter.java | 57 +++++-- .../app/controller/AuthController.java | 2 +- .../app/controller/ScriptController.java | 2 +- .../app/controller/TaskController.java | 2 +- .../app/controller/UserController.java | 2 +- .../AuthenticationInterceptor.java | 20 ++- .../src/main/resources/application.yml | 8 +- .../resources/script/seatunnel_server_env.sh | 8 +- .../resources/script/seatunnel_server_h2.sql | 2 + .../script/seatunnel_server_mysql.sql | 2 + seatunnel-ui/src/router/index.ts | 4 +- seatunnel-ui/src/router/routes.ts | 6 +- seatunnel-ui/src/service/service.ts | 2 +- seatunnel-ui/vite.config.ts | 3 +- 25 files changed, 344 insertions(+), 117 deletions(-) create mode 100644 docs/images/application_config.png create mode 100644 docs/images/ds_create_project.png create mode 100644 docs/images/ds_create_tenant.png create mode 100644 docs/images/ds_create_token.png create mode 100644 docs/images/ds_create_user.png create mode 100644 seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml diff --git a/.gitignore b/.gitignore index 1197e9bdb..9bf8b904b 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,7 @@ test.conf spark-warehouse *.flattened-pom.xml /seatunnel-ui/package-lock.json +/seatunnel-ui/node +/seatunnel-ui/node/* +/seatunnel-ui/node_modules +/seatunnel-ui/node_modules/* diff --git a/README.md b/README.md index 304448308..822472d82 100644 --- a/README.md +++ b/README.md @@ -21,90 +21,128 @@ Click it if your want to know more about our design. 👉🏻[Design](https://gi ## How to start -First, we need clone this project from Github. +### 1 Preparing the Apache DolphinScheduler environment -```shell -git clone https://github.com/apache/incubator-seatunnel-web.git -``` +#### 1.1 Install Apache DolphinScheduler + +If you already have Apache DolphinScheduler environment, you can skip this step and go to [Create Tenant and User for SeaTunnel Web](#1.2 Create Tenant and User for SeaTunnel Web) + +Because running SeaTunnel Web must rely on the DolphinScheduler, if you do not have a DS environment, you need to first install and deploy a DolphinScheduler (hereinafter referred to as DS). Taking DS version 3.1.5 as an example. + +Reference `https://dolphinscheduler.apache.org/zh-cn/docs/3.1.5/guide/installation/standalone` to install a standalone DS. + +#### 1.2 Create Tenant and User for SeaTunnel Web + +If you already have a DS environment and decide to use existing users and tenants for SeaTunnel Web, you can skip this step and go to [Create Project for SeaTunnel Web](#1.3 Create Project for SeaTunnel Web). + +Because SeaTunnel Web needs to call the interface of DS to create workflows and tasks, it is necessary to submit the projects, users, and tenants created in DS for SeaTunnel to use. + +1. Create Tenant + +"Security" -> "Tenant Manage" -> "Create Tenant" + +![image](docs/images/ds_create_tenant.png) + +2. For simplicity, use the default user admin of DS directly here + +#### 1.3 Create Project for SeaTunnel Web + +![image](docs/images/ds_create_project.png) + +#### 1.4 Create Token for SeaTunnel Web + +![image](docs/images/ds_create_token.png) + +### 2 Run SeaTunnel Web in IDEA + +If you want to deploy and run SeaTunnel Web, Please turn to [3 Run SeaTunnel Web In Server](#3 Run SeaTunnel Web In Server) + +#### 2.1 Init database + +1. Edit `whaletunnel-server/whaletunnel-app/src/main/resources/script/seatunnel_server_env.sh` file, Complete the installed database address, port, username, and password. Here is an example: + + ``` + export HOSTNAME="localhost" + export PORT="3306" + export USERNAME="root" + export PASSWORD="123456" + ``` +2. Run init shell `sh seatunnel-server/seatunnel-app/src/main/resources/script/init_sql.sh` If there are no errors during operation, it indicates successful initialization. + +#### 2.2 Config application and Run SeaTunnel Web Backend Server + +1. Edit `seatunnel-server/seatunnel-app/src/main/resources/application.yml` Fill in the database connection information and DS interface related information in the file. + +![image](docs/images/application_config.png) + +2. Run `seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/SeatunnelApplication.java` If there are no errors reported, the seatunnel web backend service is successfully started. + +#### 2.3 Run SeaTunnel Web Front End -Then, setup up configuration of db and more. -```shell -vim seatunnel-server/seatunnel-app/src/main/resources/application.yml ``` +cd seatunnel-ui +npm install +npm run dev -Notice: -At present, we only support the following scheduler systems: dolphinscheduler, more scheduler systems will be supported in the future; -And for easier use, we plan to build our own scheduling system in Seatunnel. - -Here is a sample parameter configuration for Seatunnel integration dolphinscheduler: -```yaml -ds: - script: - # The path where the script is stored - dir: /dj - project: - # The default project name of dolphinscheduler - default: test_dj - tenant: - # Which tenant been used to submit script - default: default - api: - # The dolphinscheduler user token - token: 12345678 - # The dolphinscheduler api prefix address - prefix: http://127.0.0.1:12345/dolphinscheduler ``` -Now comes the crucial part, this is about your account security, please modify the Jwt secret key and algorithm. +If there are no issues with the operation, the following information will be displayed: -```yaml -jwt: - expireTime: 86400 - secretKey: https://github.com/apache/incubator-seatunnel - algorithm: HS256 ``` + ➜ Local: http://127.0.0.1:5173/ + ➜ Network: use --host to expose + ➜ press h to show help - -Next, execute sql to create table .(Your must create database first by yourself) -```shell -# Replace `username` & `dbName` with the real username and database name. -# We will provided script in future. -mysql -u username -p [dbName] < bin/seatunnl.sql ``` -Now, you've done all the preparatory work, launch our app. +Accessing in a browser http://127.0.0.1:5173/login Okay, the default username and password are admin/admin. -### Launch it in IntelliJ IDEA +### 3 Run SeaTunnel Web In Server -Starting the back end of St in idea is really simple, just run the main method of `SeatunnelApplication.java` in the `seatunnel-app` module. -And the log will tell u anything you need to know. +#### 3.1 Build Install Package From Code -### Start it in the command line +``` +cd incubator-seatunnel-web +sh build.sh code +``` -```shell -# start backend +Then you can find the installer package in dir `incubator-seatunnel-web/seatunnel-server/seatunnel-app/target/apache-seatunnel-web-incubating-${project.version}.tar.gz`. -# for build code -sh build.sh code +#### 3.2 Install -# for build image -sh build.sh image - -# and then start docker container -docker run apache/seatunnel-web +Copy the `apache-seatunnel-web-incubating-${project.version}.tar.gz` to your server node and unzip it. +```shell +tar -zxvf apache-seatunnel-web-incubating-${project.version}.tar.gz ``` -### start frontend -You can use a Web server such as Apache HTTP Server or Nginx to start front-end applications. Deploy the built front-end code to the root directory of the Web server, start the Web server, and enter the URL of the Web server in a browser to access the application. +#### 3.3 Init database + +1. Edit `apache-seatunnel-web-incubating-${project.version}/script/seatunnel_server_env.sh` file, Complete the installed database address, port, username, and password. Here is an example: + + ``` + export HOSTNAME="localhost" + export PORT="3306" + export USERNAME="root" + export PASSWORD="123456" + ``` +2. Run init shell `sh apache-seatunnel-web-incubating-${project.version}/script/init_sql.sh` If there are no errors during operation, it indicates successful initialization. + +#### 3.4 Config application and Run SeaTunnel Web Backend Server + +Edit `apache-seatunnel-web-incubating-${project.version}/config/application.yml` Fill in the database connection information and DS interface related information in the file. + +![image](docs/images/application_config.png) + +#### 3.5 Start SeaTunnel Web -If you want start in dev mode: ```shell -cd seatunnel-ui -npm install -npm run dev +cd apache-seatunnel-web-incubating-${project.version} +sh bin/seatunnel-backend-daemon.sh start ``` +Accessing in a browser http://127.0.0.1:8801/ui/ Okay, the default username and password are admin/admin. + ### How to use it After all the pre-work is done, we can open the following URL: 127.0.0.1:7890(please replace it according to your configuration) to use it. diff --git a/build.sh b/build.sh index d48afbf41..6ce3ed7f9 100644 --- a/build.sh +++ b/build.sh @@ -29,7 +29,7 @@ DOCKER_VERSION=1.0.0-snapshot code() { /bin/sh $WORKDIR/mvnw clean package -DskipTests # mv release zip - mv $WORKDIR/seatunnel-server/seatunnel-app/target/seatunnel-web.zip $WORKDIR/ + mv $WORKDIR/seatunnel-server/seatunnel-app/target/apache-seatunnel-web-incubating-1.0.0-SNAPSHOT.zip $WORKDIR/ } # build image diff --git a/docs/images/application_config.png b/docs/images/application_config.png new file mode 100644 index 0000000000000000000000000000000000000000..eac17bae6a3885bdaa2820d1c97605ee293041e4 GIT binary patch literal 392200 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%0D(6>+RZm&g6@+y&v{ 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ξg2Iq8ok=tng$Ew(htX%%em|<$lxt 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 literal 0 HcmV?d00001 diff --git a/docs/images/ds_create_project.png b/docs/images/ds_create_project.png new file mode 100644 index 0000000000000000000000000000000000000000..71bb25168b3f8f4dfc9a63d786334df811f824d3 GIT binary patch literal 202373 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
+)BYcjw}gx228 zk{R5$P02sltV?L{e0b&B{q&~wD9%p=mPvVP+Kk;ODRSZ5mF_)KFfoKpB}jLM`i9i} zX>P1?$`G;&PNrre$7c8XFAGKhjRRad?;+O4v#BmUm}kEdP7&ESTH22V_khK5PR z$KMaRW0rsxgz8*Ido2;5unAYo-bdi$nwOAVA$|u;qA+&A6(T;q(5 z;ojM{XIJMAwu*6OcsjAMLmMw-2-z6;zcX=0=vP+{RM`3H>er}dFtsQ&c%9CGwNwCQ zyzLey(OrZU-jv$9?7BLC;_A8qHq;*evWqW>omvDl(yhtas(3a$WuyDPjC!3^Lc-rB z`Ni*23z;*aLFO(sR$q&7kV4#zMiOZT9xk14dP#-0@hr_V$kO*_QuyM$q(xj`ZxeB{ zevWUJ+q^VW*y*0d1!!Kw%J9+vGvs=owYx6fx;*){q*b3UdwyY|zai(tx-bogjlgOEXm4L?gBXui=}?$udK_T6!hV& zw4L!?`~Gz_sKC^SJ|wYhEhxl`A2uNB2PxJ`pF1%ZwTy*vW7SJ(5t!LzyFr_mXI*OR zP}21(yLBlGD_g|5a<6x$@X|RD{F=izUC@dzpT?AavHwhy;L<>gYub|J1i%Qf6XE>bI3Jo45OqRzTWY7(`kIA|{riw6Z~BU2!7%#up| z?k*suIx7ebmoQ7Rmd{W(DKLjFT}?l8^a-i&-4!=;wy@6S<|#)|S6BN^p%z0wZzXx4 zX}Ea4U7qw_Qbid=AA}9}?-vWh_U^7%GK8nP>iX-uwWAu}QPvx`#%*o4+N{RZc1w_D z<%}GF4dUM0lt0{`uEAQ9qtUa4b|Ku7=Qu;8lnagT#9%}E5Tct|<3XgZ8MnBs2@iAq z#fB;k>illnMv{g|&Gmi%?|nT-K)Ub^ntag9gd$|5+l zc(1ns?kWN{q@S7!GGDoFt9Xns5pcfT{KClt^8$!_EAeyskm@Zcv9w%dTP<33E9TSd zA~=$ZytjCq_~~3mX^Z@~oeg14YDasS2Pi#?i<7$6#kI*9ygoj+3I}bUY|%KlH{pJ=Egw`J@*ENqWIsFg93LH+S>!h`RIm%@OWH>?#?}@BDceVo8m6Iwb0c* z0(YJMj|xSC6-8Nt`xtXIQ)Nt_3UP_&Fa=#2AwOYY$*jVV+QSRXs!KS3lhhSci_}{w z?N(Jjp(D{9u1+vAHaBAOi_OBK*{tA;AF}K}jjM=HX;sM#Ikptx*KkpMub>KVB`|yUC5X!1f9=3BunY4W-9RrI zSd+(d*IHu5c!T#GhI0(;xzO{aqJ4dKOKV^&XML+zOCUerV8DhTM7QVWtPu|L0-Cw8 z_hj*)m%svMeE(dO8ge!s%Xwywgdf6Y9bdsmk=MJ`P?4bXIhKRv!*M{Ry-`R54EnC6 zny3A9ywL9{V{4dpG`G&c(hFf^cD>}aMqt#o8_F76iq287lXLB_M}USSQN==m<2FNN-kDWsRet5gWWIJyBRs;;Y=qS) z1`jce-2G?{KQHPVg;F-dP-2n-`nv0Le0~S$ak_3c zO+@!iG3!WNM!higb_D<1v(N51V~U+cR1xqYQu@qNozC{I4RmB|)%O}yp53Tx?}nR7 zV!0A>^xZNDy$De>wk~Smz_WS)Wyank>Fpr#RYx-a18T|2BFnQdg z{IrLno~gyT#|3Q1B8nX0;inxs!XM_CJ`E5`1r7KDc-V@NmNk`sJjGU%e>8cBY_zAc zdA8Mvqbu^E&@k^OJU6tI^YR$CjZye3&yC*O4%jNpa;xgnR3oDAHYRloXo7L~nIphn zR%@`I z{CtaI??O2{O(*o;hQWZ(-fk?*|*}42>WKefeIEu@q2fdy+K!!*dwFEUVa<52S z8E5D|mO+bCZM=4R3pF{J6l+0|d$!(!MN(OgWw>xuIt*^Sk8@hM(H`apw39XJT_Ag) zc(RRLx*!xPpj9>E8csb(<@Ppg-}Nqb-dJA~jOW9nkuAFuCB?!Ml`g(h?@oAZP<_G| z+j(P;uLN{Zt&5$@vb3oieea@H#$|a-f@+4bg=$d4*AjADZv=gV%-za0Hdtv>@LMjF zVOYBHi2;u`2 zl)z#g;`e#09cAO=47j;?+D3joZfe5VqIS>=vo{KklqPKX2ZQ9IXyNPApJuNFXS4FB z5f!woRvK1XCpXHm4E?>cfCtfk6<0l5XQ~H6X4mnFOGs@!aXB8P+;t?s_K=)vz3>Dn z3lZ-h8WM%$g6{F*#wsTVCzKy<{D>nhnU6$2NzpH^jN!aI!?-ZdIS>Z4)EIU|6j?KC zq21Xs>(|=E*58%8dMdu+!D~KT$i53YfMdkY|3(7(cKW@B%;ig*RkEiDA;)>`2TFuk zgSJ*@UV91AHvmmAuf06fm2{tn4Hi#iC-T7}A@tho?qiiM-Qg9UQ09oxtAz+jx_dOJSWEZBEu<@EcvFG0zPvL`mU>QmJ|{a8OP1X{YjO2H57wO-hbBgOWClva zRlbHc&NM#w`exL`YR}`)cEY7_4~Zz5R(9rM7Pp|!K^PCY*zh60my(s-FvAnwh2h)? z3$;lGD;lUI3QgqEyP=Hlq*3ukJ;;hD7gE8;qxDl1D&cdD;-|Y4*Jvo!i}AbUxw-xl z#7bsw+b~!74n`7gErofsg+=jcBQ`9x8`mfert$u6ZtsPjQXxH!GX zR3rOt#5QVJCVH|iT^R18U#>JdF+^ggCiHnSJegbXa;P}IqUDIi-Fc*fj2mm#$lit` z{Jx2FhfDc!f9E_iKa3i;f;x+f1~*dAHdEaV^ka66G``R#mEP6`ZZ}NKZOKTke0Y|Z z-faN*?KXSXr(3pJmzi4pSeK1gmTu-sresaQ;kuMn`4Iav%K&r*(Ysw-gPb?H!d!!B zZXo2`-o!cAmUU{M&(2qY_lH_IQhNyjo#wctLjPNp%OS)cy8vRWGo6gu^Ml&z%CVBh zWk5428)MgUm{>vfvpkPiN;%}#GSIZRu~y{A`OW3A^_=n^x2ICTuA-UID#6rKW9@iN zR~d#oLSIao^EDY&T2Bm=!crJ}VuvEM8;L5WOIX<72HmOxKDOoAUg4_2Ei%qs93^ z3)ui?xOTJR5>J20J%Z3?{XScNB?EFtSN!moKPu3Z#j((QjTyf=*UhR_^DEDo}1ccI0)FuwV% z?ndItv&rF9_oN%8jLqnH_we;h^rS6xUROzbr9Ax;zw)rxAL6e8&HF; zy^A4Nx-5TqRtB5@)DLSO@q5-7Xe9!6`+8w>p7L&V-LpbLPHis-2pDCbP8rkkF&0Q? z3^9II3ahUz;O85X-(-n6VuKA5`e?x{KXuPkSkl02s>{u+JX_ej_<^ruv2mmS%Jg-G z+PZuMZc8#-31>51o43M=#X=OIb}3=W$EK=5T*a|K%SU`>^YAPsHGMykIo1~?tgPklW^*Mg}nEBD4b&#j2$AwM)NB`f)N-Y)@0y3T6Tfi zr1jHJyyc8At@0VlUFM6}#ZFTDrGQC-Tkc9g32=IlpIRHNZN2@#E=V=X50isuyH;@2 zA~Zm8%R<%1eXrk3;Qq{As7z>S|v%1R{jjV0eGU2_YY_Ek(y zA0rS_})?mOXiYDaRuSSRPp(etwqpRDW+R7H#}%e9TUyD@w7FTjj14{g?dlv2DW#nA z=rsq4Lw>ojxruT--4cV6s)4sE>hlcUC}}q(DLJZE7R-+tQ?pfQA3rU^KbXqv222=% z?Z(1-@y97v2rlyb7!CDWm5nGBrFdpkJ^?ZHbrDw_;UuO--jmUlw;=Sl)ERTel%_kG zs@B|gy3(?5YbD2|MsdZnrE}|9IcQGmrVQ%B3F>Gvmx=$ZuRmU3*V)wFe@oyxxp8OH za|%Cs2}>Sh{-L_L%|2Xe(;a_b0;e-kM<9`6!xyK9@zsWMp4&@A`Cw5 z_X%qoYkqo-DJ4)7wBLzOG@Nrp&OKdkWelq~t%i-R8|$2|7nE#(@kk#>>m-P9`<`sqnh}o$ajgGxs`@d z#dY}(nR*|THMweiI}>jzk6Ko-b?vUBbHK0|SaVB=*bqVTIPJAi1og|n$MmgT2LFz_J>X0b0BrCTt95{SEv9S`gEa z9?oq*Q-HZx3)KE&7Jz?xPKQx@>so#VPRY5r-uqy;+cA$-z|U$fhjG$EZZM;?isZXG z$JaJ|Eeg7j*tiu~eO(|3<&B0Al+~Pt-y;D>NfWI+AM<1g#8I&L+BlM`de zL-{X$P)W`t6BDE0qK(#V=qg;BwRemC9iR=g8ylJl>LcXG<|wG&GsFFs5&w;{&5Di=^8e2N);pjJw4IVLTy^ zFYT>6>t$ms1~a=?35}pQKlHCNx<*>f7Ns@v^Jz~FtC8fRfT4U&MGLdzEi-7Ny{PSK zD?c&n7{RT>=o{<;!)@b;FJ;@m7cG8CE1U0Gjd}0aXbei)60q_csamNW-V*IybN^fG z^#7@|4Y;0LKA?Xn?>OA!4w$RE%mvE$g+@RC1AHV#yz?R znkF!MKJ>3VELECHmA#j;jq)N%Z8P7;yRVTk4N2n40MQz=Ma?*BB6B{;^p1j^`xP^@ z{gDSp-B4l=1yp=pAIRu~YsTB=P$D3{;HA19!zf+9F`4ZuP*cI^?Sh)g#cdOU5bx(Q zoLo|yue(&Oio}8CHMS&3lv+(dQ8sb{lAB6Ps|1!rjY;P>)bDXLw|~<3?G35L@Z$p77%sUN}fOmrgw+m2EkzK3=bG$98<7X;JxH+qU zIhI*5f^;#88$1JuIC02e(Oz~5FGo=OOpguH&ss-8S@bM=hfz#ximhNj01_`+OX3{{ zHkyY)4ErJZkY4k1m6cUvzMz^ z;`wLhG{wn$>%uv<mYKaN0AX6Grg;mh?(|fl~cyTcKnv~XH-qMy9d*S*Qz)qZf#{%k~1UPLv`1tY7tU!zp2mo?+wiCMrj^mlUn^KH93lk zu0Xwj+RA1=SZHRcRO1yz0Pb#Smbw*ob`JU|9e#Uf;Tqd&mw1k{uJ=p7iQ83b@=`o? z4Gq~UyCf_+sa2s+yxz`SWH2GSJOD&SMYldZ&lF;yrvu35|oNSI4rWhUali%^q_}vV!cB z*-oNYrj?D!?{mYKF`IAt)({Z-j@%q@qx0CpnqR#pIhQj6S^=^Ox;z)&-#0sZF_;@V ze)lD<oZ*ub}D<{!W${XNG5I zV()TXWw{@a6C3g&Wgra3cZp)WjZ0K&n}eUu4|BUf;l>R*MOHpo+x>+cqQ>gQ$7Pts zL1}U4UcpCW*>AiQ<4p<5bZKm#BO&nc-|)byIQulBilp%ai)#0WNW z#iy5eh>33siE>~EJAS8|>qVr~+TmpZP)vW9n}d&v5xx*uv-QB;@Uoik>hzcOoL2#U zJ6X3=cs^VJm-J;YU|bQPB*ydDyQ;U1G>gqPz)ATklayu6v)kisMhoj@F9=!9dA)LN zb9NOhGs#c`jQunQ_WB8hG!}*t0OG70tIaG~TKsg~O5vQ@fQpE|KyS@%&hyR1eSl)!RrcD2 z-H$0{WT(=AUpZMo=trS-L7o)o@+tGIk@zu1Knyp{k3L4n?#<-a+kIWYCXQM+D@;nw z)tQ-&BMYe@89ko)$B!EqRqW-0)_;_k+mAb1Ygv*O2ZVUTLUkGPw{Ospn;+fm_>}<3{`4nQDShHNb`5wxiFUi@n;S?^NHQA&Vbj zwnaNkkJzB{1+>Fp@sd){6G+sH&d*RDt@tVK^DlkClCFD}m2+?%R0|!st zsd*lTt8SrQjR~lWogIH3uGF@|=LaTFoM*ki%m_p0Hyp7W3NPEMlV}?W3SBP`&5B~l z=;jLkZtTLxykJpXCQPf9-^?iFoWX~Ny;Xc%HpH2R#SgwO%gDA)h8Py?&M{=Y>XJ|b zB5y32_`%IgQcJ}Z8R6 z!`1DLY?XPn)D+`+?~gJDX}Wsh`ZCH%=|?@7ZM9N8fV89EfG3dX7;1VaU0`xfbgk8& z?QrC2JNl>XW0OTQcSP*ohqkiF0XY^cmFh)r`r>9mhR@9+IgHr5%vtkx%adBafmZ_P zEnq6e2q5qG;aUF}*m*g!(4Ln4E~WWvb%}rVsGna7_ugPW#DKXAy^C3w*aXuxJeD?cjqdHr9AoQrZc*LW&5nqJT~U zb(SKGyy66pm}SL^+|m+dy3zhGJJczj3v@+n<9y7EnwVKgxc!J5FeRZYV`DGKO)AB9 znOCvZi|x}WVtf13#vc33cy=V{%(6q6y7&0v*ql~qAL2aSM74&xQYPj%pQo*rdC#lU z@v3(_3Kzx5M0s82%u0*?I4iYuCsDe#q%iw&-f##oC`H3-b~ICasW_SJNZhKZ`_(>j;Q;kV8b{0L7U zr#E{)s=C9Tx*2T89d`nt>>kn z?=QX5``i>$6RF{=HcumkI(Wpdq+;}(EhHX)AkB<35}l_S>hF#y$A+uB(!1wgsn+LH zsE|#l02UaTPOF5z=Zz3=s$g3{5;$R9GnfTs7q!R5)o|DfIA(1ava@~*+`DmkXQdw0B)DQfpms#V2UjkMp(>4mjR((r3KTQL%vK*9X z)1=W@7k(FW@+wZ3KZp4++CPdjcpggWn7*E^Tqxg5Q@8UysVKYXPy*laFK#rl?nM~> z)>xRGOmooCz<;gqj)Xbls|}jb`0d$-h8CiVr*$^x{GORUrap%@Nqd4b&^>PKUzq6f z69vxu#k$!nHLW6oOFTwS>9a0I;iui7KEOZeA_ii+QqEsVB8iKxXr?M(X{jD?QEtj% zTPr{z75R7PW4fcb1JYq{sX(U^m+o~sa@Gsl7d>~Kz^ zCfmOFwpoWBqF7^~B(oD74C?C&@a`^$NmhO7pOEKT*p8UmVi|bRIgjt$8ehkTw(m$l zDzb4E4Lh-W$r2a%8$@&Krt<~%cE2IKcVY}zXj@A$wv%Rt84W>B+DUONsN{gi;~D!V zWljULlwhl`s3>|Ldaup~d4B8NmCXdf)CuiccRStD2GPfL=fU@0dXxlXhd0A^k+Y$A zS8jzXpNyQG^e+cM+%DfT^q=&}sY-DyvK(mRyOh7RZD@E@pn#jdISl>+2#L@lMd$W* zl___UkFd4ws;TvEsW@U4T1{fvQR@Y!0|ua|Xr!X5N5MieFHDf@+X5yysLq z5X2)+POE&m_h`y?WNpKyfa>qRcnB)0n**L)`*O2yfThlT-1Fp5_&c5Sn*s`VlInvd z60O6%cIvX|6@92df&i8CT(MfGPL}_8=EY1+JW#`F^Zuy6;Q^G6 zf@-S(M-mK?<$kOir3O{L?bg*wMCETTq(=?rAkdr6b;Q=!ovpsJBv$H018m2G&NkRY zxr6b;9;Fp^Q221D?OQmw)!|&B0M;?R$9r8}CewN+uAaX# zCNq$kc;4|6(jY73Y7LDxz(p@>>Qa*>-}u0$rc$n(G6s}}ErZ#V!5+fa2^&}Rs}!$H z2SmnXRctDV`U!wg9xhH8s}IAQGv|z&J+z8DW{6WyU)lI{!ih2;I3=chO3Ken)fH!P zc131%%XL`~DLG&jG#S|15L-Rpx~pN>%m!yChMk}3kpm`i3hMDxeyw0+jS~~meMtLo zSW!r7qN^e)LUvP(F2}7FB)*v+qrPPrHRic&K>vM?Q@lek6`U|g*b<62FBnu!aO(bC zQYwx78n0nXnQiG^%ri17YG-!4ubInb6Fn4;cH0s9T`lSIHbvTZGJ&o+rVMi=%ij|y zl@qlA(}^>VyK3mx)K)!yprzldROy`0gG8_VT-#1C;pFok7sD5-erpeu{5KZ9Fy~p+ z`;UsoM|m!Oil!Hgu6LPg75A##fNaH}9Wns#n)y0x5gvaqfzCO~0iThI07lbm87TW)X#HN6^Oy$Kn+pl?ISdn3H=ly z_30v@+&uJb$-&@Brq8F!`tOwmOoa*ksKw~-5cJ3gb9eAdTV0#tar10sil$zeqA^d? z(~Dk9t+-Oq{P3?s$NyP@S@r;ZUJP(X&mY|E-;?>l+5kHgM8Edr`}e>6p8-&M#$z== zU3)4^F!L`$s&^j1TFk{a1y6pG7Jr8;_%hHr!oP8*=Lc~81DwOAsq>_h6_u4N8y`~s zP&jQmr{Ut)p}cK*0W~u8hjJuR!FkS%k#8hUX0smHqZxfsuQ~R&8k@hyq22*H5-IfY zH3nu--lJT%Bm2Pha0PFapN2av+=PTGdz+-N9L~^;an$!ZujA z%4sCu1_hVP#kW-UV{4AnQ*NE!_2Js1{?S+T*Z26NsQ`0Y%KZV%_PTYiv*IuhAjceu zNf7$-_WPC_`;AWre`kF%Li9ld9)DhVh$wreL2koGF&ZWt?wt&-eIxUXM5A33MQ_eM z3R-zl;hI_w3dqu>7u8i)s9kIQ#_=Q1J6M~QTgKWD7@q8NF1}L8&v2iwQ}Qj*sN3?>&o9AlQ_t~BO%(!R-O%7E*3yctYxSv$zPw&F6^ z^G{1#Q}=dO#CLtN{=ozPSycYOO52Upf&#gh0&3@8+Vi|lP&BGZOV-qa?vt~Tj_4C~ z%o;uPXv>rpdh&2iM-sP(7pV|yl4~Qk%-x|HeLjz+H}%a;qps8xX4|v65lD-4tsimD z-`Y58IJp%|q(2Kh`-Az9z5kd7!hbx}%&<0^Lo?GXA4MG~K>eazyg71g`;qr82=RQ^ znEjY`$on&N^oOCT^%dp5!lR)(g~hW&8L>ZqCife73~JLPH9craw%Xi@ejWe~uo)VD(+f$9OD32qu_%eg$v-_F~hH|5z3buxgSA0@KY zhV3zn)Rb!f90vcOsk#~yaOutATm4}toRCDTVI?oGUMJBbtlUa;%W2>`&py-@hvs1B zlmffl~Oumy>CqCWh+tk1ONL}%0S?D}lKvRSCn9_<_0HX{_Eq{Hv;wL!% zpTcnXvgH?svhxJo8P)N_lY6(ROq2rM?Hw$Ar5w$l9l1LKV3EnP7#hyB%2F<4pXjYM<||bNmXlibuc2!Lr<`-zNF5 z+`8p_Yp6Yfsu}t$d{V$$Cr~_3d2ytO_PjhTKP?0Q#PO_ps^g)Ml~a7@8vG68E7qP$ z<@r4r*)%}^6UZO_5d7)n^MpC~^ACA@3U1sPS$<*Y-~h=kT-m-h1*_CyB4*WRiZ3~g zo?^a4II>)xe`B=PY=QLTx8U1{Mjft9dj2V>`0J>cT!ezd)FMp=<+IZ-v8J6|5s*^9o%^ zzCUi@eR!F%zeg|b{Rl+Hva?hCi4$h$XBEZYd-I~x)Q{Pqm)sPiOPFm#S<@D;I^fe@ zdDosVYoAd}XZf|$+OC{FTYa7LVkUkyZnls9!q?&{Cp$kg~gH z{|&`|hx-2@_=X8E0}TCu`Cobtf8a~}4Kj5%Agw$uK-4Xt0}j_4@$g06e zJ@~wIi}T>pWdw5JFNpV_;N3s8g#G!wInVi}OHWkd`NYQ_dFvFR?-(0bn1edpJe+?~ zb}YEA%b@KJ`$^qzQ>8g>E`190SCIcVvHYjv`j0;ZuW^ZFb-0Z%l(i!iNUwhFG6(2F zfq)74XX2Fq!q%*(^#lPGdg8Ku$gh4K-6-F^^wG2Hja; zlCT8Zd+R^L{bQIGO-XO+XnD*L-&p*CM_> ze5)8Opj(^h7wG==P0mqUD4ZmFzqvKy-jhA--fkn{j&+}VgP46GT!KbF6XsGopPC|U z^(*(*tEa=n@Jr0O4cEH3SSToa%gX1ac!LLl)reVxc@C%A2)v) zkqmaYYf>2muMm*2?)V}Rhi^K_qRQ>PJQ{@mD9I+fQE($(OuwYiG3S99#!+)8Z0vVuSPGe;JG0p4h2C^3 zQD?%1Qnxoz zW6hripWGiGcDvv7lz4x4XZ!x==WMtFKK@FlF-{9er36IHW;40x+og-O7F9)VbFD>d z8F5XUPGijka6nVMbo$lgX92r)eqYz(n6gFkdbv11?TD^ zu)};u$GVV{WLModW@N zq9Y{FGlw6$RFf)-9JklXa+>FGSx6p^;>2BxYWEgUzb zf-Gth{a)UuB5Lskt7tWvVxg+f*^qw8+pXY`@_u*nL}NSsY(Htk?dpl>?s~#a$p(dF z*Cry)`q#oj4+UST!CewOb57JqJ1-5_wcDz?o|d`lZGpjyQvZ3G2I%hZ0rZR_4uj7Cv`pjkVd)2p(}yp z0CV60s_Zb>z|%mjUwJ0`b*VQ-57<}6S5QM)lxQ9yDh-{Lao9t-z?D_VJL@s7BL(}} zrk^JG>HYk5R@P+?50B7T2ZwNv!l3P#BKLuurQPPx<4h;d=`ik_0xtw*9o0N59QRW5uR_s(>6fZ?NMAIY+3{DbkGf?K{UXD12S?EC0D$c345{d(T5)q#TA4vJ za6+PXsc*@>TKscD*IdVJHO2u-9g_+guW}sN#x_dsETGknh(Z0& z8|*jL6uDz=&cwqanOFR=$``$WArnXUR`$q^CFqV@kGC*^6C;tPzw&4?tX3zUPss&k z6vj0IwHdDK`P-%1-6;9OcI$31HhGm81-}4#-BFOt+ZD+m4xb;XgGD7P$p&r+33P>& z&b0yoYS%%?QRS&eW`%<}q*>N>FF&3jw{*1EBZYnN;vTlsKS z_u&x4)U+u*N;TFziir#Ac|RT;;5}I`d$!aLv6L>*Ikxgzi@y^hU@zu2zdC-=rz?5? z3{IOnjSEe>&vXNK=0;HZ>6#dR>x5qsAIs-Q<1g379UMYFO&A*+E7bzJi=-UKD#Y`G zwlS{Ql)cgWn+yGKMv7}iT>|jcelwVHc-*;CZEP$HeBp6VPtPkzjCo|KJvyPRe{F|J z^6C8iyj0-UYFSP{S^@8~9(-_QISU!69e)w?e952C^!N}{Q}*wc^;DAj+U(hzO~Pv1 zFFIuNOv^=mhFm=_QrDAoRiq35ANJlms>y9zA67sV6;TmE5D*nmk&d*`6dPSddWlF0 zy_3)aq5>ia3L?D-0@8cvM7jus9%?{9I!S0D5R&kF+4r2Y*>~@A@7ZH~|9oTIKVXEA zH}87anrqHy&h^YWg-qfk9Skd1B146*K)XfTK8$1gXP^nkOJj#DlYR>I3@R)~wrr#AK>~~?B z4Y||BA7oH!#V7v0`xrn%SC{hnTd~Bimw<4OA6KUq*w5fisRm4^HS7XraZXwshRZ{{ zpqH8geX{sVpUeYoo!?h)%MI^jVana~Oj|Mq6PgIf9Su_xJprGs6%<_=3_FojVRSM~ zPrK^pEAC?eai{%6(Sr^jJW{bk1duxCrR!O*f!)e>KaA}|e#Jp&=YQ4OB4tNxgQ3KZ z3gX6m*9kCbn)MV1N@1J@2zma(ig~-wo8jEsKB&9_Ra~@IcwaNR=n81{tg-4+o}hMf z8=3c$^B=C{3cQeWnQmU4MUtOLPrXJ#=)vXQ9+gv>35`;FyL-oSqb&cHwoDbH>rBOT zxX9saD+0|U6!`E904@`;eX7j{g4$c#+jowk$D*g2g6BD_9Vw)qi3zUhDp`R9DJRsJ zM2U^Xgf~P$<46PU2tcG{4#&$`)B*~lM>9q{GD7ISAru_g>5U ztqfN1#Vub?r{A>dj~LVA0(u3V2|A#`%JrPwrb%CdfSIA#M%A5v!o~k&ih-Hk$I5;# zK`T{mbDQf6IBs1dBaZvL4T~6`Eeod6dbkgpqMt7ms4R~iE%(w%fAH>y5#HW%V1Z1r zLM5Kjl$rg7g5t~k+;4+8r0f^mCsikVKE9smmY&({)T%{y86jQ+`E!zTtKVop?|kDB zPf*-gaRaD3R10VLe{8H@DCqz(9i{-YWS@cYBKJk{k>mmZVD4K0$dDVWr2TL#>l~MY zy}s3b{5V|JNGP#kXEp}BXg?&hd}7RiI{mh1eEi~Z2yMi>=8!P~AZHxo!(v>bCz-+PFgC%lp0_hj9(2*A`HWW$_d_d-dR#U5*I<(O(sdqG zBPPl9*Fd(}owFAwsy*zih?X5O!S_PB7@WL1;y6!5%#+r%{3022+ui|ma7HlY2RLwF z?ks#=BaKb`H$FsAkl?IQxRLX;$1D`vGmbhbVFbIe&BL$R1uj<*;O_- zH=l6P{ab;?A8J6{wYD|g+G(ykC5AOI9AvUvni32{ZY1rSUR0+#rB5xlu_^StrE;&0 zG9qI z6TZYG-NcwOwGodA^Go&O{6hLJTv)MTjAE0xp9K+2vgGL{t^NLkh2L{m>w^rF?g%dB-4&MfxYk>Dy*zH)o zmYJZet*sqP%#;e;KTL+7%$tPna*n{K8$08~*U=BcFUD1H={W)*-(qgFzwwJoRO8pj z>D8RI_@4L2|8sB(97gyt1Nc;GwC~nnv-z^q%l{Tx98@^vas`#(QjEfXhGQxwJt;;Y5x&(Gi8Cm2qa9z=grs=rR>yjU ziGS!;p;>U5TPbUP??Vom?F$t?)ndh=MI`+1d7KBFV-abZ@>gz#5N!6hM48eiAP7`D z7F;)PfN9p?$^-kb5?CUTOe z5hoP38@5vq4CjAWtS5O6i(A9oifrwc*SGQn&t651eLgDSLBMbX?Whdz;G;zF%1nwE zx%|l?X{81!&f_jqD_46iLhNu3Hum->^^E-tJb@t7YL7BgFyLzBD{G62P*AYiD~Wz2 z8y?iEN#03r1vz8Ck&LNq3-tW(Jg^n8MMjCdr0tmqrujrT6yW{JUD}%82fsyE7rU>7 zaxo?#>`@49qoLAnwoUiF&Aa6uPGSC2Zl4QMnSksR#+1ErQ}0xE0Z;Dx>&#H> zn2w1fNc5+?P2ZPCMq{NfJnWGf8N6i(gcS`YxMre&B>re@bab>4?J(9@V3Bt}ob5vu zt~&7>47PiwO^(`Z4m!~Ss}S7%JIeot#fu?oJ1HN-bjXWpA?6b! z_G56Hq2mACZUT@#15hMFNtPpsG8lehb)a7>7-()E3CLE4+*sW6(`PpAlK?CXooI*-R{$Tz7x9<)RWBw{wN?kx5V(5&Q5eIz9 z6Z8xAnt$&^FjW@7VkSps#-)8dW5s=R0dnMZ^;i*}iyi=40Ji-jKxZG(!=L?oe3Fxi z05$oW0n=!lRd+HcbzKe2UbX@HXV2mP3}pq_p81;p>4Znp-&!)@!wXKpBva3$75>(2 z9;VR&8hn`CQpVo-d(ZlR_xm?W{r~3kuTjhY$>(1yfd7-1|C5)$;~xG0M_vL*RE68% zvB}b`szMFv|15*;fbpWe{EQxZ*bI91ujS+4Mp<)ir>;PH9BtW68E1a7|6Qy0>RYmW z7hJm9Jvjb$jr%9VzuV*W2M`{~B{Te7|C=|ymIOHci#N;f{Jl2_ znD56n=jhpG4}R}IASL|FwXNijJy7sn&VxPT15m({fhvms5X|Fcz6Pbuvv_Cs=D$A> zkQg9L=Q>$>7L7ZSs(f%bJt4pn@{8mgT=^cofN>6ExI@R$@PdPV3Q(v8+-Pd?SX8QfE=K|alsEW zSnq*bZw4!Q&*UhMqWKQo@E`vV2sr<65`T7p%i8N;zSy&`fQT^>&J6e;m{WR_OLdQL zEpYb!`D+f-P%gc%-{bv?VXBNj5L-^Y49v5DtG>y(1J}^5!BJYXR40Q8hfA<2vIv;LWInMno=E0wL%N1ZH#cpG{ zibsAwTu~Z4-pmlqbWu7s&bX?$nCI#GFF^yN zn6~NjNts*KU&rvkBK+fy3%N=%ui1ZmW`cXT%N>|`if|7aecsT}P@Gv12zBP|X-PkX ze64-~y*Lp2jTOc91(MkRdre8Umo&62VO(D~PB!rLSRPoR|5}3pA-Yq<`aY%CS?1jI z({d&v!v~bxx6`NJFbhaYyjFh*vhp@ht~jeUc%+Pp%f2ftJ+4$>ZE+?3_mw=!8PJ50 z;E>fz`-`@RvqFzn>aLkBxpLp4{JuFJRnY{C)#me9c^@egut=O)8TqZB(m!`pwZNm5 zIUQJp;CSIj2Nr}w(w|oOg_0Jq&2w+)=z|ajIS6l8!L0*q|7X_6pSosNELrt%#cSQJ zJCL{hOoYzW=?P($47bS=ZrjS}4nJ_&Kc=58o~DlBVHW;2VQ%>FKQ!V|cR8EkX`r2t=iWqv?G{6nSWsQq-tOL?#)YerygQ*hR; zP}1mq7|NXc;OczL1r$|Pc-X@@pLlLJ$hNf$cE9h`N<+g1s;K?e&Q4&3DQwSDvH%FS~?;FX>|Ja(>TUYH?p@_+lKY>t0#i%qrhT5S)#d2-5eueWaXyAF@}Hi21S7T&YIq=thYzqr>i0Mg z$feHpo-q!Mmhg`J&<-F3*c~e=F`$=`A9_Ik{~_Yn6azNva<(UQrgjYi>$)(emHD}Z z39ccX1jO%}5J*fI2aG}o{PpI6(hLDSg6}6@-0It|xRF!DvgX*Ce|oi6;kE@vrOga zrmj-*p5q-@a&rwu4k~IO7jD1$Lcn8<19l)F`S`Di#2+R&8_?EY6rvP~rK!R@%nC7C zr6lgRoBNkgD60TX6tBJeRR6nrzSJX3Q&-hKkYwHm?`!j0tiAu|(H;A!bP3KR0AI;z z$E|34`Z|SQw{QBRCVIQCi7s5H9IzsQ1w8H~!cq|9G4Bz6a1{SF9HnrjDIcT2Wto~k znx25m0A9QI=;Kmbtf?*Up?2bW(ZycG0TabS_}z(ZTk*0<^88CC6`A1nC7JqLlt zuO?mT_|XGTcPyjwgL|dqsS1q9pZD8oHv534x?qn=EN0RFI-pCvRt`oF0~&ywDXx_> zf503!RKICtzGc^BJ^0DrrvIz$VZ4V9VYSBd_r;(i0T2#zR4(|yVmyM=1WIKbAIjBg zX8AK#e;Nu*nOWfS;Pg+~1J7~ESsK{rO$;Lk?E9er$K&TF-rq3BTZuz|H+cUFM~PU| z1mbO23YZMr5@Qc8jxvzX;B$=eQ^A2z{p+g#;l-l~VA*En6}%7N6(z;vn;u&!1>ss1 z84(oE3-N!}0GA$}1yn%ZX0d*{iGu*b-^>IYA#C6M2jTkh7&z*?_~70_)OJlRU}8dU z3F~4RlB)eL`To}n7C;3Ni@be|?_I9Gc3{en)dFNZZ1(@~9Dn(*|Ic{SN(@JiBm2so z>t1K|9YnbQ7dXZ_x**L{R`Rsmvv>+2P7ka0F^vA z2mjLr|G4tTev0MGqeB@7ci{mAbU>&4YtHvyi0oG#`ESPVKe*kor$FOr=n z>b~zVX4t8#{oxk>wLmu>e)EB_NW=;qaL588fz$k$*N?Bz{dn*UXyQ2aIhgzSJ{4xi zT~+I!6Q^_IXY14GQ?(s`lEsr|hw(OyQw{wdG<4pAMUEId;0vf15ssL?Wyy=boh;7s z0E|%7Do~menVcfdpyw5)>amqpFDvY`T2MXvOwHQKj>*HLyuF))>;RAze>R}i+kzPF z9&*{y7e=8HS*#r#7+vh+6B5K{bim*k%z0MeC@+#kuCC>z_p}6908`w1i~f*q?-AaZ z*E<`m7DED-pEG8^eOAlPW?&?&A!C|iwkJ(J;pmPa`n`9Yrkz~C+ zf@qxs+68!u1;jU?;YwpQVYzAR7Z(`^5PaX9dDkw#?PAvVB?N$fDj3JMaHnGzrF$;c z6R=LfbF{(hmaJe;GihAq%;a#x*?`06Jg@M`z2MJBYq$8b`E4L66`ix(DdY$KkJq{V z4WH}J*YD!H^Qc(xo<^*kmuq$V%%$T1-}2oQz=W+?X;?LDY6QAYaqcq}fMY+WCD%#n z#P)i}PN|hJxHxT^W;8+4;p8M02P^%UQZ?PbPc&#RU^RNvK-gPT+=D>-W00FMaUy1j z#^0Fb?l(?P4tSG8+9I`LgpMavoJ$eCRDV9%Yc6gouM~A0Aidzq@@*&I0~Lgo?Tm~f zqzm@C2vb$_Dcu)0=9slkrjVQYnS96*e&nbkD{OqH-~JISKnKPvu82bawa5Sai}fR1 z@ReTYT~3(QjT!k5PQDb59nd+0n!SLd`$YcO5iyS+A6EwpeapX_(rOQWQokJg34xaS z)L;FX?72^Xc);zGHx#qVO~v7XgazitlG%>+QT?j*4Cg)-{@FnT0PiSWKZ17B=caDX zD+cnaB`=kYRJqLuxkdV{MoY^;vRF-CG8WdB@Bw_pEj9OM(rGC*B19SRxW z4{gpGj_z6~oa%qMvr#(vl6}{0Q}h8b!)V{rQ;MO{lBYb~#xhE|S80v2;rrWCN>RRQ z`>7Qvj;D2(EjF=)QnlB%B7`TP-ncnIcc-A;xrK32nN;%qTmppe3`0ArAF%o6;cQF z8V7!PJUob#4;nXM^M5rCpaN5N*A7S!h&ydMCA#)|fXJOHPtkUJ3qZ7ULfPG*hQEz+4(E^(T2aAOwLRJbPTNa%<;dUDieQry zj_?lGt<>Wt-@BFpHet$`nsC+UdY)z#K zo<5*)PN_Iocf8D%nux&lv(kQ3jr(lY}C1q4A0DARg?6*21CI*O-3=W(Vs@d#2J(>DL z5MWjv1DI8Ao79F{2PFn+Bw`M^J*_idBBx4!??GH^C8r1N)A=sSR<-OInI+xK%(Nbul zH~4}q%^I`RK3R%Q(ag7!6mM6;2`;fvkxYe)Fu1gx_RG8q?!RR-{iSnyU%Pj-X9*MW zW2|$iqy*pNw!T~2?q*FvggC9c01Z$;LQ*oQ*u1q?!OljxS}nLu&`2_PJNj91vzG24 zKm4Yyp%GJ1d3!KDfcazl~g8g`Qr?bVG>Tc=I3kohFkVcL+#ugB~J0_^iifD zCa0AmpIUUC4>8EWsAA4xtjtB5O~&L$KmCz)^h7z&sHCT110>oa-Z&QM9(68Eg?uG%*rSCH#4!oqB?ju5`pK-Q6zsnFwJn|CMOqy5G&VA_ z(~s%QLE7|Zs#7)>`y=bM9wIJMbH@Qn`+0yw$OgrY#%e*5Kq`}?t6AXW2h zz;?~zK09tmw-}&Ko2?8NPc@zpKB>6fz*LelZGtpY*01$+k;azwi#x1n`SIfb0!ryc z$b`^Ve1#pcpeNB6bnoW7cUSI^zdbe^K{(g}=%VXsgogx5k&6hsnx{h&ynZZ5|CUb=1Vi(WY zkBL%ntRlJUC?sM#{v1R(qEN8cZ5>DzYmegdAdSXMb|m{?$E&Vfly<(!Mn;iCi_+9J!Y!&%o1WKf#G6}x4Kl7d1)y*a z%|4wRk;H}>I6+$6T?;3=qKe~eBaX~aVny32@2^A>Nj|bC+*kBq_-G<8ZO>5N9se-L z+P!mlWlL4ff-42ETc%=%gTIp%57S_nE9aAaHlW5L{(jPXaQw0}QhZ|1cO%3JW%0ui zxq(^{t(f%l(XV`VdxE;==jmE{t5hq#?k%!H(tbR)>ZmbTkF}LIF3;uxR~*b3(R=Cy zEo@I}?$|VZNgaBl{Y>*v0Wcx+v$=h^7U$iDPp?MCZ|5nxlQTPoZnfiaA%&3j=Pf|3 z>FvpCP zXmj51m#gN^cg1{QwX6kCE0pm4?+3By{9JJHbFa!-w2}C3>ms4Q@X<(;FH3_W74P4P zC^nx*t*kpa)rvM>h&A_W15KRK1QE(sBu~r=i1?safS)||pR!x7=Ial%4X1_IxGBHd z7Hzj%{i)qkF-Wqdew;7oJwpZ3hlpSA2pz(OnpU&*SD5dp5>>coh71#76JApePH-c` zXySJFBozv!i0k>vZV*5tZ--ca10H)6V(_-{&6{g2id+1CkzY&Kzh4R6M;`i{+iagm z&0zAqi6Lcxx%-O84|CtFYAHUqR0m8Zvo_{67YWZwAt?^q)2#J9xv8!v*D*jEB_~aY zjX%IeZ#DJp4OF>wQ*m5Rx_*zqP30MPp3#_!gQTc1b;;e2(Sk2&$GwrWq5oFGmRNt=2+Vf$d8ch$ZDUlhSxcp^|7 zGB0^??`!2|q*^0vZx&oKsFv(~gQLe!VJGtypjqQHw`I2ozsie5o^tpHw-wsHwVX(p zI46W$S+_TG;h~xV86)KGk?M}fjh53+Q}`(-YRq2zj>*p5v!{h`LXvhqttJy|`5B;0 z0^=VIz;)g_l~;CXrGj3Z=qUU3N|Kw}bHqfJyr$)Ul8LV{ViCF1CmAk37wg<`R57;T zQ9KIeX{<7T3uWJLf__MR9uVrKp@FV~fb z_Z#9#C4bNoNA6Fx94r=*t^`}j(R61RZVDr0p zjo3&v0E)tv&^@n|63kl8Iw=?NQI8)o~4DfiLKjN8^YV-r$no**^v zuf$6gjR#VZ{MKGwbJ==5es+?rL$DOI)3gR7l7-HlfV7#<1P4wZHF+EDv21!`h`imF zR8Ot{WWU#5KE#JaMrmDQy$)b ze-ohy;Nd^5Y}x{}U$`?Z9|Wc@>BSi&f=u;&DsRO}6zNNYa2)v1W)5E2UTiP}hd9D~$Zod|P*iRI>eRa`oqI8p6dY`RP!VW|yPwAc z8wEr5+em}9Ece;=cK)RP)1^-+I$T;f z{3O6&Pnw$)GeuSk<@wrJ#+bYz=K6n#ZR<2FcC~X2ZwXDgiBK_#J_aOIaUZQV(^vNF zFAi3Kf7lI)knRWLvILR=%Jx{O`<}jwg_w4_m^&SGuJiT8z`aTZ%FH$TiMh_WDBnAo zd|@0QA-B(>n}qbM7e5)z@uxJ)++aYofXQq3BpL5dCXhd%C2XHcQ_7?oovYedj7-;n zZMBhKVIkF`wF~5cGPGwov|UXpGEAa;(e>@EREWzKLj}d}s^n+1yWJw@ich=kRL+E} zSp_DaH8rjh!?%bB-MY|R>?^ZNDd?Wwm?DW1>gqKws24A`gn5zj;R4yi&IBenp7a_4~JMyEJH!k77^ zP$yA)3lg-+_Jr)Q{s87Ejcv9VpWe8PxX{MS==*>u?Cbb+;bvv5%;!BIfThF=)acYC zfp~F}ZgY7;Tc}C*1v|fquc6?3=a33O-^n!|KgLI-U1x{&&oS-oQYXIf`gvv)<(brM z?H3*XnftZmXn4w1rG?Ky6S*%h_1!&lnuVo>Chgep>9J~D&T@|3$lxAsvK&ht8Z^##I3Ccs8#s4Q4kuZ+Vq4#_8%Bipy>O%WR`?$+ z8v8ZBd}CvFob%jdvGP%yk+h6N$u|G~Rt?Rch;XBBhao~|=OM1#7#}1?EAcD1Fqu%_ z1FFNO`Tr^lrQ!yf(Hy(<;;()w z-Dd4Uy4dyFeP>roD?V!1S$?EW=<~h8NxQR%2`M6C4!cLOF9@lO!dcf@jiZ-2fO6DD z=o4G+Rz7fe7p{Cguk}zfd+tvzH$N?A=J%5iUSO|0l0NbT>XR~UEGQ$B!`aJ!ZGm97 zo0uBXawj@Yk!mg2wWqNm`4chh=@8gtv(LI55GA zf$l=FUw~{A*VS*gqn^5FFmq%uOnpl5%z;8?rmLZgm$S3mEC!c!Q2p^9io#OG5X0%| z897l$UCTFnHe?Lj5CM*Dd+VQTin%&5zSNw$xD?-v8&G2e5-}>YvPdQT)^!hcBw|| z&a0!0VgZeD1`i6WNVWo@rJT}Puo3G={(U2doM2#Te|*var?2i9T_(nk%i(dYzDsU4 zy|*109L{dpW%m;u$*VRCxISfbgn}=^^%oc2b8mk~!nZ4)py@*Fs(wYP-J@Nb@D$(Z zJrk<&^CLaGlzmSRX{5;I6~9tVKX1~Pr|mxvLs3DO)vaIDD4e>(UHbX9g<(D0P9Pcg z;Xzt;f9Jef40kv6W!f_I)=b?}POf|3jifH58W9Pu8&e;0Ia)C0K+vH*&0V?YcU_yW z>D~~xnERWJ6R#1yZARlRZJeldX<4v$cI^hPM)PMH=-A^iyP_%5I}VS&8x;u;%*#Y> zyF+`s!-+vBT}tYgOnUr%rx{A6QI3rQfPkJZS6UkBe&Q3zek*4z>I^uSS8tU8r939sF}R-5UFe+IHqtd8|x45<7U;Nh-c#qC|Iy!#qS*ka@a{4BvvqoQxNIqf|2 zT@7vAvB+-0PX`g1MDT11BlA2dfLDsw9@WT^THmC(wec}M2mg(BKA?Dos3IkGkJ z^U&rkAprqiLF{Yat({|#=oi~kZdG1-s(Q=UyP@gA>Ls0R+&-P1^bt*cbZ)P3RbwGX zb;eYp>RCb0OlRl@09J!2vDKD5~uxWAUuLo*9 ztEg&PblbSSBE(u&`%Qj1uAW-G{E{BRE$iOXPLnNQ?A5}O{L?;P;PK_hj`$%h{-l!U zEvgXN@z)a{mV{{OPddLW#gfqZ!V-r^^05ZJE8S27bDAj3y*9I>kTF6$BjLt|H6vv` zOVK;6Gc?WMsS|c=XyhY(HymX4Qk>iI4sCvl&z8Z?a;UJOBJQWXPaV`@Y&1&u5GA1W zLUEGn&};glk%ke&eDnUBc{tauTSL;oU*Kx@3ob|qVpo3a4(-fJ+Q@AT@NOYK)USXT zh}+x3e#Xa^?oP8DraOI%?xMqnXeTa_0W+cU31p^O~Z)aa( zV>f>LYh?e>4`m1beYvL0>B);8-r?-!SHK?jGQb)e0CLL`9 zf?C>|KQ-%0VdA3+n5pBYowVyq2sAZhRauWUpc3R4ARN_*s z%&`6)%O>>CnzAwp6`~jI5TJWl@;Gt}fO*pwMS~+K6IGwIm_w}oIVKKz3Xy)ZO+&{256Nz5~bTZlg{T^ zV?W}Wc6;2r;_LaY68LwE1k+m25PJ+lM`HJtg3WF2Yzsx++ugUz?SiSk{hdD=R9(w+ zP%=i`a0}Mm56WAq*U2TCOrILXHZ$@3}wmiu3*b{iPDy;*Swdme0|fq-y``x_On>a$`VXEdQR7MqZo=0DcM`#Rt6RKZX>l zgIpR_-&;ip0I!+TK*V9<=v%rSUHnCc$ZcFRD9A{ zxSO=!P4W8;NWqPJMlTLwjY0Ja#kBDfkNSkDhof-nRqWg85$}7?E;W6}1rL$W#7g=5 z4KpssBq&G`Px+8Y@@HoNfH>C^W|SfG8$Ej!uUHNoj8H(e|Gzm>!QA>CmSIge2x zc}f^Vk=p4x^B+Q_g|L_U)iAt}9lF4SYh#nJ(3@s1=%^X=;m6ucn|kZ2Wf}~;$lAO@ z-xDp$ueTSuBcR(F`D_oUsBmF^ub$0ZlS*RFt}I30x9xBR*5RArPk-1#!N-p;!g_F9 zxW#iDPZ?k?{(QEGz59&p-@P|vaxD{fXwt#9<5IMX8tTDa=}8hwMjcje>F*K6DR3kTka7SKCh{WZ+X@OII3y|zINa&0c5A^ym~;(V#q(nQ@DM=B?doR2(eMI@gU zdBG+yT5cz>G1pz1eDbr&;*Pd|*}}VLT?w~hfSBYF-}bP?axb zQZ9=6pf1{(`U}UAnwZcLx)Kh3cMVN?e$Z`6F~tXr8HVif{3Yhz$Kj8@9ciTG*9NKR zKR;^i1hbUGL`Pl-ej=_uU2t55{#oCgm4H%MVDpo{;mebuA`@E{1wqiX6 z^=W!30>b*{Lg&&ifE*J$lca>7<+1DUV46cT>nIWW0#tJ22~CQV|3*>tg`VtZX-X#+ zIE9mBJtBo-OwLP6gT^%zAA+WCqN$BZucIO(6A|%iV)17kKzsF@j$fLa>152rRK_xD zmt*K_3NFozgX$M@2h)B>n*>gMRhQS0T~!S zmGlyrNQ(!lW#CI2LVEN;4=EXyRJN}U9iOf*8{B)9i#>!DG^#Lc7~((|0vu0`Teyuu z?~Oop+A>TWOT2{RuQDddsrn7+Vb{HC<~gD9m5_Q&bBH5()&M(E6vPpq) z(r$d!pd5^r#VWo)H9BYg8->{M8ebpfQDB~m-E3!6q>`fu+LIOv+#=6jxMg|dnNnxo zFz+cEcpDiHm1YqhX2*#`WWj#e^SIRY`nks@`9@%C;onUAztKFtEN>*ovQL%x?sTTg z@7<1pwa~AKA9jGsIRAVhf1Sm|AyE}_Su?V$KM|W1Cp!q?U!OLt+8vG9+iL0>D?UCo zdM?nineJR*`ges(rwvjQ8MZxQ)&2|_QQ)Hunphqotg-hp^E&a=4HsTd&h>Tn-BSlv7LVAcqLg00M zT??(5W8&UJVJ4fXON>Q=XD)ASn7TzG)Gi>ID^2iwOE_>00dSi9ne9wVD^nEUhHqJ= z46-yjKg!b+O;SJC*B-%&L0$AT?Xx?V(3Y=xM0i{aQQ^KEqzuNx8p)R9W58 zDu}-s?@(^hemCmgI6v4|rmvr`&`LG>#^T68G@n`}&b%nn>Lr<(S$tG4`>dR{PZ8m( zGGGYCX}leC9dXI!Nb5>&r(ce&_TFu%PJ71cTElV+tm;4f z>YMiyTxZ{c#()@gx$)43?D8W0*4upuW;WEaZdm=1ct-0pDx#W^IG?A`bte>L4*jxG z;n$9Gylz@sS-KR?`Psj_jKjuxD?ACbVqng(ZHG+O!qqQ3%6Klk04nT^T8K~H1nR4; z(cN(xtu;^}JI9jeJ_qO3KPR}-pCjB&bjSAT2rZ8rpc9l_8TlsdTYv3+%@^)sG~n$j zwnvv449NGN{woXj+j{}lbmRW!L!J@J!KXq%*@i_5(CU(;(V_8I<^8gZhyvRJt)l|F zTR69z+ywhkXm6?=sC0`mk~6fYY%NedNLB0@v#b>JLE%%Urhx2F_edj4%N&|Zeeek@`QGW#P#sWDjcU7Idd#*;Jc=9 z9`mK1qT96DIs#XmArs^s)cO{7RHh&2onIESsAw%9jkamOd;7Gt7j@@Jp<|sf$6I>u zn$QV~Vfy`6pYJAx{nsIQRPW+L*oHNS;B1HG1R*`5MtX8ZOII~=`P5Cuw|&B{4X^s( zbD|gHNBa5K*4B7NEi5|>oW_ck9~jhmzq7W|5ENHOcRjM433``&59zgfJKLatw+OC`?$Rgx!8khqDG_K0AydegJir z5+&bp+&a5wKTZl(-W63c(%4Pndok>1{5Xp5U6Mya+%pHp(?h}`H(o7l{j(OpXzQu- zLmPT6Z!dt)CAErbR&w)Zm<(oXkI{udlEtrh6gI5jN?`cp7aMQteA~i1U)aEc=(R!fym{g+JM<5rY+&LgYsSq9r}|DkF>shU z;rOkIhlI5?hO`UAga@J2HJ^sYBkN6NHmIDA7-4>;vEFAi|4!I0WzmfrF3+` zGUH=kTz0!Qa)z$)xP-(|H^wruifeMgAMpM5H(?I~oH+$4gP06_c9pk+s_|cf1}eut|1iGf94K(5cd%{xz52gm`)A3IXZW zVU-m^rI`jCtrJ5W8{`|L(AeG3Eiq=_%m#eJTLr%#3Q*hwS2{a2&i#mrW@_N1hTII2 zg0>KxIOiIjJ~zG}Y)hWa*LYvy-^<~H0`19Vc>`6aQ10nLao~p>Qa?_jp%uN~u!%y{ z`N?PjaWU^*r+L7yK|xl6-f;jYwzeF5iaDr6LV7kMIS~N&#mr&2NtndOt?r3!NJW=3 z!+5xmmX_ibN2&bW#p)|YM9Z4?Mu$CXghS1O=#%-0Ik_bvptg~(S;Sxm2J*#*@pwg> z=jyY$baoGUN)wFOQKQyzUNFh=Il~K>tl7U6rbCAn5ARtBmim!krYM9sXXnOOJP2qNA&!*iMCg@NU(HPy+%xv@c6NMwV{nV)@R1Z|!8$7eg`z3c-{8 zzCaJ-WG^2poSf6RDJ_z)0G`ib#ufD9Ib0ZPd-jsw|gVJWqr8wXCxkC_MI- zJtm%hp&f{BNU4X0-HXAvDYzrDb*3HdxERF@oX-8dNvkmwvd7jr;tZ^<$`tGPl0HEj4ZehE-}j|f zi5D`L+d>o=s%>3MAS1w&-;1gn39$9%oPmgcI&y;5d$bDTI&J7BA$kZfyOnLmPqThE zyUv5rdfo*)1qL;`t#RG>nYKEEbb_xK3@0q+wRBGFs1(LCeSxs}h_}laM73{*ByaQ= zn`iT#CJS^E$kiTVg`D%V1TeAdUWYn4$}|`jW1xsyH=c(Imu9Jbx@n{J7KbMhI1+cP z`8HL4;+AnhV4CH$00OXQ%kfA|N~_}2oR1g4Rzn+al_Y3d(roS~temJ6-x+k4cf9X^ z9f5c6tYqdD+T}3ox_rfXLqnSlaFYJrK^tBtc~-?nQ=VXlJ1 zE-u>7B*7+G7)gE1UniW4CRxtq@t2-ZM|;Au7hqQ!nfkLrCS1P2tZdLbH~21z@T-+t zb!8~;Zdq|O;LoESvh!=m^Vo`6Ip$5%a$B8|dWVEOsoNeqCbI4emyE&+;l7#F)mK0r zY;e4!&1K%O4S=U<87wg7Rhp^MLh*{4oPq2DE}ry}YLkhs@pm|sI(;`7I+vdg^cgtoqG>HNl}D(nCvCI+f2+j+@7oJgAdOK&Ut*aWhCLH zGRVa%Nt^dM1-+vp73xxNzaqJc33gQr7<4Bd7+^7cmbM+%R7=ZjX}E&GU_`A{X8TY~fGis7?zWuYAyQnK`u!Q6`_L zq+Fds_?KA9kmCEL^e>3`NhvYMvmN>x5nhaX^f>)GxmOJ0>GP{2Zeot)Cu2Oy!K&hJ z;RWV?PSQY8(VG(fPZ1Gu(0;yVjl*V=4VV8Ss`3wLkaiZR{TQ5g>u}Fj`3oS7@k5}n zjxYL#Zdr0)o)$;BxzaeNsT*PN3HmB)=+hNCM|Qf*I8DsZgojC;Zawy4%U-+&5QlldOncfc@6H#>wc{PY{y6^N16=7+$qUo0 zx?Vbi2=cmGR&lGuVnpkqXh9=%wL<9piN z6r>;ifz<%xBK3y+Z}BAXWe%k664yI~)zsWD)pysX(Ozn8T-lXQLVXBtTt9s9S0%*> zi5Yku(J-)#BgSu6?Zv;gg7$8Jzivy_`T6-E$5t|HFTb}8=&5*WQ6->zjIRjZb}Fmd z`&}pa6UKl66T#@rF7Bdv-1N*puo`M)tzoc`e#^7mTxh*=f z24CCJuS@$L65qx?(tzA*93xFEwIi|#`|)tnG^C$Sp@oy!SC@s}b;c;|^$p-VENR`8A3uLShbf|B z&Etj=l5nC@@{2niLZlH=f48DhN;ys#$=RhL93Nu9z=^lskfy^q zfhtRJ(vueRmB0xAjqYIa66?sbh7oTCqW{fkXsZ8r5ALHBW$ednZOhK#Vj#dxxh$LN z@2~X@o@aU(bUH1~cF>|~{jES^hWEuyrUCQ%o%?(m$tw(~`cbl3E#R1F8u9%(PWVr- z`(5R5S7P?zUR++nDAJUFD9eui1PZ;}lO$XxF)iXK+29TB{pLh0hvg5^nPaM<nI6TRSW2u_-+9su&)m5JOGM>-R z@qGxBbIh|fCvOzDFTUDdA_5NcP`-tVHi)Gy!Dyb$fTH=|Czi4?VNNMcH9UI}F zzf4?BDwhfzCHDHtE}yWDpBc3)xP|;~48dSl9KY_mg|_Q1p{^EmV3r@a z`C{;#d%`PQThlX5_~3~K4p8B3t^n6X#nWD+UIz=A(f9$!>$|S^pCQYvHRl>* zj&|Q;6xd7n{@f$UnhxNL7bG7+(|TtTJnOr34zn}W^6zWszd`|kzcpWffCxP3yVToC z9TT%n%xkpvQ@>SoM|r(+F+!lGt)?X%noJZ-5vBH`qg_+hzVcY^5H)50_XR{g8Rxup+YBu&ho;n&gueBGNak zlz}sql~28adj^06D$kEHUg5>@KsF|OGnJ82-||s5NWV9%GsDF?m}2vs@`BhL4Fjmd zpnJmjZcgK-J`Xo?ter$$snqb_d3+6-&h8jK$UB1H3YbT4ue>bgw`O0-MpQQYrA841 z?EE7eM@U2Sb;j13*mdtQ+2MuHZrLSX33ZqLl>ok3p(YVH%3FT+e))jTqvaZrH~5}f z!2&BN&B5z=D`XloErVF9Y4e-#p)~`ABF{zbFVPp|E^+s1+`D}{WV3Q{dfH;0xOntQ zSSEAf1m)+!9HgmfKyS4MbazY09lrA=u4n7Oy0;7c-H4ACyY%FjjD`=`GROZb`IR=MdGc4cC+ zk&{EB+-U{Qj38QNnoa{|yKjKA;(zX)5VLv{qinS$@lX!Ar6y1}Wb7+mSwn#LQOEoD zX(2{GrWm>eH5m)iLS!^rUL2rS)KuW`%#l}w&uvS@llini=Fe$5F~Nnd_pvqiM%_Y~ zEwBw@c#8cLYt_lO3!np08)0}GQs#aN;a|Nj@HY%&kNwytinh_RGTLm>_G^X9eQZcU zrYAG>j%GEb#uGJ#h z9xcbfkPHR2CqJJ4*Gcq$nB{-*Z3a_Y3a&sI0Wr7~q;TTMlFEI$Bi;4icT6(8A8Gxy zf&Ul3^xMU28bQZLe65l3r`kM&o6lU$0%uj1raP^HQ*ja!5e@BxWfYF>@?9I$} zz?8W4zm{m;7#{jsu@cGjFCLkbLi37Akju|8ZNJz7Tq{26)m7Kj_4~J}!)44@EsPJj z-Qip=s2nHED76+7e8Fjz7a?qU#vI>K8fa&iB$oF6@u)WHIQ4V=NszHTN*mXk=MrKM%jj5BSkU7>$FFffqLny*z| zvat*)9UZAG5_KH8V#9iUU2k$-P^m*NQ>lHldL0%F01e}bUYa&Nkb)$!Y>RX=uxk>-g#?L)=d$VoS-}^^|EvQs^I;5_pZ6r zx?&wG{hY4!L@w(Yds+swT4zx~M7&_Tc&MpcQp!+Wb%4BUhzRP?UmrMs+s)~s15_j1 zizxIjS|5Cy|0h;CP%xHD^F+-_HmS0Nme;Hiw63T$ghGV_=1h;%_Y6hhxpC5TY;@TD z@H1Db0V7XfVt7F)J+VvCsUM)n-K zUuvZ;yGiqei-+g_&bXWEt@B6C|EOyJRExhW`laiqOz#pS#%}-mO`BpcDE+iEXv#5u zc@6+sI(ysELm*Tue*zqY>6wH*y}i|)JgP-FAsYIM)zuxI@PdI6GiZhf@?GlGk{^eTEFdD7^iHBO++Olv${Wh2H z1`)A7K$G0N^lfp-`3998<5lqBRHKk9QwFlU)`Kh|P>mcM&9JHaF79KS&uflbdyO%sL|U;Uj_0zb%wAQQ{SeyLUULbHFa-mcE`z51F-Sx4&bLjz?+R;@iz%_}l-fLacq5@NTUB~}IULM5X&-i-vTSP-xa?kYtwaHI_Lh(DLc4s&NqMzcdR7Uk&d}3pvF^^Wc9pj?UX9~TD*X*{CBIfDxNtq+8&#e?`7F>MQKoOL@77dR=2iICsx(1+)8`@O#G-!R+V0c}+)9oA7aF~4Eh|o^C-HSt-L>ipunYpz+hy&@sgIL57+ z8izpaey*c59`=e&FY;73E4A8O9^LLs@5FcbuFrOwLw#;nIJ6%>Uzt#CS}u<~RXA1F zd6eO$KeBuXQ+nDI3X?Q0`W~I* zWHg}+6$<{|7&02l7O`osclgkty}TruT`F+5YX3Nl(UfB=91G(YB(IoSP6kn>igQ4A z!(}KqgwMilq|KY1uQcno`9HG%(OHOYs3$8IY)8Ma9NlGQV&W!53%F=vdF@i5bh@~S z7TL5x!m?+g((g1f_^aTX`1o4auUA#3onbC>XCFSN3>P2%gW0O)OxKSjhn9-*dX)hy zo%Q)qUWe)4E(~B@S#ZjwdoOrN>a*WT>^_`E4EBjpMU)2VOSIPZ=JZwQiZqD$B@0AR zJ)ve}-rv4?(xDbnUrC5Ho$aQ_nCddwJXkH4Es8!5Da$7Ve=!q(+%jGX|9tb1hhX!4u~?>4;30OBJQOI&g$Eu^K;7_vFK7T zN@->d4c%MJEw%2pA0JIJsIbGqC8Gq@;y1SIyzh^1{2} zXSy@M7@4+)VDFA`2ni9Wwg4pu*AfZHT3X=V1MBIOF%qREvqHnH*AzD z_=(~G*f5j2siy2MBX*E%9xZ=bc+!YI1T!b(yY*e!VnFZm)r%J!OUG=i+}32{Qd0#G zB(ix4a6r?OoouMF+FmOse4LMiIt4>IL`gKcA|9My&mkPu6L0d6*2B0{kGDORnI@m49-${qLiEd@Chj{7 zo24#|GS%}ftTOlko1G12U1Ko#$8u)Pv>>f`B9c(npLjLm+|AB%|;M?ZOx@5c)C=aYDE*_??xX(uRZR;a`&<}molbzJvbFvr25 zm6G??^j<8Nx(r^9P0r{9o0P@iyldrzGD?;dnUgZkXC~OM2XX$7@dhX55CGW7`&UH% z-X%VARwWp)wGTzsf)`KaS^coL(AaR>s%yw?V$y&Apw|Lnm2Gdv^Eh6;?vYXG-W0N= zAovSMz5HDPyZ)B;ETa#%X{yUt_%*Da`oq95jyOs8z+&qHGkl_mm4SYQm~`>pao-iT z`EjyaEV8PsU!!t$B(Lb+SdC|jRLbLGw`^;mKK5_kM!L(9Q|ZPxqZl&p{vxtRBSB|ce_8Y1hM8y24(&f)z*_GOx_9q!y-%$9%r!VQJOy0xRIj8z6?m08cpQ4FmKWuzZ~Zf8?A`TEp;0EX4t!1Ltxd1B8RfTsXG zNmZ94b*0^24jrHSnHmQbdcg7I5s^QfVSoI1mOA+9bF;|fr+-!G^aFHT)>i}` zl~|-kF2|@!(H!EemDugql&`ifm_H`y!1e%FXfsimqLvuLd_>loaDn)PPhC$q5jHjh zV#;L>9X$i)A(H5(=IUh8^7wL4kwZL)KXP(pzBwtke% zVquIVV)u+N=SY7~*Yt+J&G;_OUh>PTlfc`OU~Vk(M)N+*cqW4Uk0Hd_6CmO_*6R8+ z?w9RR2tNHuCpX5LFDZK%w|?Kq2$mz4V=b^ehkSsafU0yjQLg*`h}NVXsOGI_mu->d z<*7YLa*-HDP=fdjE7Z{W#=K!CIpX(S>5J>1`(*;$3j}a zINj+AM|29bu9{t7Nr%P7oqdoN%g8dyQn+=v$iYPIIh{OZR0cM!bYuZTeK$xCS=M zl?OXv+0x74;-rUZ- z@TRcn=DJ)4?!KqXmy=G2+HW>c9qaf}!%(OBN5ihI zoP-FbC*KTbphs(pV?RBF*Ic=Bijv_cvw-Je8+1t+k z-#Xp}8d{wDk8dYKflw5{q)1Po`}I z%lDmoszr;+3=qMk9>BBf5ntLuOQfSBM#|pi6&^pXgK~oQ7UDRJ3iZ1*VK*;cRgFK6 zQS|ex;nOb}D6O+|Q~C+d5pGt~K!kyP{(xNh5-y-j%iYHN#CL2Rj zYx2AJj2GxP*lwByje6Nnq&H$lR=U8j%|3WP8t2X>%kY{-e4|UypsG#8bijHyI%j!h zVQ_qDc#jrPKh}!QKwE=o3y)G9TXxv(=Dd%4YV+#myEqEDxB)jvc!PY>qOFHG1HX93 za$$gjnss{+CXSio`45ZZeBg4ZE#259iste@nCGAA(4((|Xl*Fu89kXP$kc?h%A#(; zG$`Bu93Y`UXA=dJHA6kD$Glr2w4V=o*pPy7MqEbi&d)VSh(DK=LKY=SR3Iscs=oH3rjb# zc7?0s^I%?zCjeSqN}{U05pUMIu8ET4&Qyl*golSy)q1oX;*sCpNTXh;^+w2~3+O0* zaco&*uURVTrb_l(o42;u9TvJ(YO{|h4kXno%VOq9<8Bc~u8PE{QA!? z`&Sj;=2M`>2gn_8_%;5z7D-)hKNK%7yJ^wTbC|wfQ*LisThe#UqCvQzrwgNJglS?3 z`n+je+suB?sKPG$r6Y)M14rX zVS!Ui5z9l}Y|{8|&zH;95`{Is)XDnLf1;!z_K>NYb@hZDd5h&y-_#o5yV2j+5dFFD zUpz?c%bxkj>ip9|@!&QEgi$h{gf8q#9qUhL^M)WS{#&>Gw^!rDw^m=$WUZr0VgD&h za0+A>g>7&}@`is;Hve6ojutdPi1Za9@nWT=wP1R1+KegJ%`LPcNhOq){;mjXk+5UD z0Qu250fT9$1vSLa1YFAeW3JS|wP@kM-O5w}(eqwEh!dTbDbj|_^+b+g!DHtHV!%AF zS=$^D?+MQwZSP)1M~lAc_U<}bvyt>VlK~>Tl2Phvj zq9h!JtlAQ}t>jeoA;Bi-X9E>EyNqs0{ocJdh&@!qT2B*VGv9qm9bO=0)HnascKFL% zhl%>Jj{E}BoIuhlDapRjOOLSzOp^|{-2%XA%tY?A6A3)pIVvc7f0fvl*A`&(=od|3-W^~&r)s%k=;O@_jQs2>lJQncqef3iV z7Hm;=@)Q}v4Q?af?G?eqAlwTL2KpP&^#Y1dNRil)Eyq&RohegMZ27WxRfbg(e?UuD zVyYF3#qNIcd(hb3^JFi%C!=u^o-=yIG@9KUiKUPTSh@`P?;SnF@zRjGrgEOS&V_;8 z%M>5-qwOY!aR)r0<;M`^w>X!6nr^MH_m$bkesK)p?`kK$ArxU|L-C>G`KK0M(D)mH z;ELNH6s7BYMS71?m^4G~Qy4Bn>}W}O*Nb*8zA*eS;aJ&$rTFq1#Rgd><<=nDt(F8>rguPy zn_^~56sFAY8@wH*#NsVyD5Mv<`DGm}Ow4z#@$lCQSHS_-=pDB#BbQsM-ku*)mi;^V z*OsN7t!;NH#ONW*o^=0`vZz{D4S3j+Tmi&~gdjLH>yaykEc=LV;q3$L`5?g4FoABq zGmH3*zVN#T|5HLZm>P6I^rE_Xpk|{WQjU?}|1z zR1mZqqpx247Qsa1TTdhMZ6x=0^^4?a#*$W89V}T0h7L$O+8>R?6EWNFxHe<&@zeR? z0O_h(&%9nf+LsdzC_J+1>ie;M^H0s0-U*6^%3aBEP$6rtGqvi3gs#w!SO?S*^ZYvPc2<<1K7ATbI#LlrSzS*%SrKLI zjTh7Fj26l1ymM0aJi%vN+UAW~vXuVL#=K)u5`GBgcM@~3iZEhtfsVA*{Q|EihP(rW zuJGlitzz>_RuuHA`gwVDM@iwW?PaaHXT*spf9C&K<9S1(c6EktRtOR$VmJ9=GQJ%K zxoU~9>QfHUoMK|gBsI0^r`e9baoYb;Rl#Khp%TiAZvUYw{{TeD5H_L+j;k{c0y~phU zcs_%qm;cx`SFfm0=hK6WNrPW2BtZ*{3({U{eO)(WI}#G^1$XA==9WN6Cotvq2{t#p zvpe2pIi*NxRXFrQoq48nwR7@`x**Jv^xAzN$EUCLSualj5CGnsxvKK4+@!=JccW&N zOEVc{ER{u3K=I%w{f=vU<=h(y7tKvFdc-1Och>bnYA^};DWX2Q8+ISChSoBI$!CAB zO8XL-aeBW1IJlJg>!D+dnzwjZ%JLGw zY;vPxU@*R&*jUAbwqnP8tFK89E_H%5efaw)k{!!OjYa6qhWMs;Vh2lX*JpzsWzbo4 zgC(6%AYRNFk0qS2O#|6A0^h$u%lyqNcoV(f9^$fG^6%5Czo}O6DAP?q*2;fetzzO& z=16_*32KZTx0iD*Xn#??Kj|p=_T@tmL7b-oy(52JCDoy$g$+ZQt!Ew+!Q~0U{wAiOgVhNnP3v+vWvA=c0ws<9B#gdCpzZMU;)}mL z9(YED?jvOmuJyY!DAfJjnCvV-aPCXL1py+i(e0rj_~(uI@ zUvH?sWM*9xCPWv0 z@Ah!i#1<{H|JA}2zMs3NuFg9ued@`^>go8>^M8_oe|NzD--|g_K#n8xSqAfe@(X_T zZvVbBeFub8~krRz6IINs4LC;MBrb|@c-oF|6BII ze4l^Rk6*X{|Gn-1g{=KQA^%^TEdM{&?Q7>OrT-Tv+H__^8FIelX0@bP1F;*r;B9`;P|gSA`E!2&492OKeJgeDypORGjsONse@Qvo zZ+iE~h)67J3JE#e|xM&(eKii*lb z(Fa%ow1<*erNFpL4>y#=W6Yu(nV>wtLgS$QaqQiU;dWYvWM4Q#x4Qez2Y6= zAYDq6d_onA*j=akTUUgHly`DsGeE06uU^UcCA$A|BhP9Dqx3#@M)J?cEQ>s8V0nO# z;;Xn6X?{ecBDBJh6_0gLI9N{k*|)~t>qD^JFxew8wFI~BNARY zcZ!GC+M8*3XmThjq`OQkKR9&aK<&S`bASF8_pxCCBDYOGD}57tzUoKmy4v)TxR`=b zYliuCQOiZ~x0E`DQ?L}lsi6a|Y!Ol6Zr#UDr9-+AGzb1C&dBfFm*Bc{@8M(Ilq9H< z2F%M*QvPYCF?OJM?!AWt4SY*C|Lnvkp&!z|B@M?PwLX2_9{JkMY5z`698kiqdB*>I z-?hgI2h-&Z;sxgA)J=`wgnWRY_wOQk&oH#qvhVDjgw$4Lfmg`+_g%yJLQ1_bE=6q+ zZDjZwVR-*bGk7hCI*+(LJ!NU#oa2gNNlRHpPz!5`aIU9CskeSI=XU`+$Z$Yb%=`_F zlZ*A?tRN{RHr^Q+@D52ZnWZHF`Twt+mFchsC{wktHB-YDF7nwpb7GliK22dHo9PV^ zYtkKCF30~*KVJTuM27bhbPF~x1T`vuNN3BK=UI7XG=DlWdi&>-Db?&ieSDd>h z*f4j{ovZb~TL9;qR_e4OwS<&h-@4ul!>}m**3^k_`-kctd0RFz-+2NsN&L}67O!vf z0e!zH=X3ne5#V2V#XiCX(0}v-_g97U*B)xII%CBzG zUJ`%}=wd||FUmdWW)6Oe>q;%j{4bXiq#$>suA0-h4`Q8}0`ZjyGI>3fZF}`Cvs2>fAod(x^$)t~LBcwXwKWm`D_r0&_Wl0` zTnB;!PHj0nr?ZO^!IX*xd$E6$z&2tC%Okf&Fx^>>S1C~pV+lC|a>;a~%QBUw(DzRq zFIvel*x=8`GD?5es?{Zg)R3jTTM9|H&pFDcWJs!l6JMrqiIJK{WwP!5+e7Xzc#P{5 z=p!0`QyN$kwQ|&jx5Abk_W}ZzypNh1GUdFax5oxAbA%hC`h1(ZmW2cUn=`V}e4mPxU8* z*^=`ToeRIc(&vxOCicj}4#TZzcF<`^f27hr!P;MBm2@A_lx27oNaOx1Z;#%;?_1$0 z{_GE)%RfGzWuzkcRl?={#-w$67r%dxU-Sd;YS7_A4pZrzrt|5|l}CBH$ajl#Ne!2; zT2?l5H>-l)H-Kf+_u&Owi3!n|bklF+6SFmAu1>C2Gx->X`Pk9yhTwyTEfy>AZo9*- zxsUQb>J*vqtFGul5Y7|zcL+(uTEfHPr!=`%JD+rlpT^53G zA9e@u7>q6T5+T{%B>d``Y^@oUcxcuZ2U}&7{0P9AIwbzw7M=#U^Bpf!k6u2b-*Ms+ zhWGGE_A<3R0CSijCe||nOwyHF@0ECfZ_Typz7L9|k)AgBrV2EoUe`OK3oc*|Qo1ln zLWT#+C_NYD2%L^0x5t>qasqKP>bFHF%I48Zo0Z5pIkNjRAw8*LLgRr2xo^hf{CX9) za%hx;K1~MPJjbm8dqc;-)lo9jSx_)kq^G%D=WZq!`Aq%N8xiUpwcYYP8%Zw}&Co<> zKw_EDdf8SJ7YEfy{29t`N%$W5msuY_8l1AVoqc%BS7+noeLx9PtVH+?+RYCa-(h!5 zLH?BuQM{vYu;0XYbMuq;Y-;UF%t$#h^LuCr{D8OHn63N!s066vjFC6Lwg-Mm{2+113lJ>4RDcpdMufAniO{@4FFm=ZorV!gRzN zN8&Pge|8G4Pq(H89`)gvXI~_J!j%ubq&*bI3re){D_k07A<|@Qxzm_RVO>V;JBt=* zqQ6OKXlo=hxWL&<&#-?db-AC!wV#}75T?8WIiyb1tZadVQkCk{r*t6q8(uD7efkDV zUgLgM3ABc&4!hu1;|~Z5>)8p{v8qy@N11i<)7Ak(?h*d_=Zv&< z&xL`g&jq!;`5e#~W)Z6zK*N`C7*?P?F~{LiP3xzbEZ4LH8-j8yXJ`&OqRVoPJO|%# zovL}}|1dtMGLcmTK>GN07EBk=1(%05mu$zXrVw4SMMKxPHFizV1*RM>4%hVx`5Xo6 z9!PmEyf2tq9zW4fqiW{bf!WHHd3oamBdp*O%O^JXHv|l%2eRO8!Y)~aHFLZsa+H6~ zYpM%zxq5i_+4XXJ)lqZAv2sx#$V<-v(?I^&(2HAAf#uzDb8@HC@j2QXR_n9pUS#0n z@v?oUrzU*8UoyZ7*s@98N)=YMdb(Zw!5-`3TU!GnYjbfTYb)C{D>osPjn=Q#TPyd1 z)>l&uFJKlbEeE>>4RnylN4xVakN$Sz2iGz5@E!DXhZ%32y!z*a-`~Bo;Cwn8ufF_u zlS9H#;|jGGC&-*OE)|5L#9(>W)#}}ib<{)a&P|S?ecnga{GCu9eDlR+ahA56#$eMx zF_qhv5rYi~Ku2!_RbX2BZuB0gTR0PDAo_@lmxJ2i^dBQ$1kug}%G+x75Z^9;pp}LS zt#!Uxx$Ic_WJ-wvMzZYzV554AcCGD=epG%WT8bmzhOm56Zuc5^jEW)RM%XORM^Jn} zR#EfF8lJV6fGYnFB=P~FYK3!TrQzSg!vO&x^qhHgEn39ye3>$sb$owiOO z+=hR&`u_N%eaASbV(Vv$Q{c|L_2T(0iHGaYBhDp4B{*6(0!^IGxCZ|G{sn*`YSiJq zbD%HByG^d2QaZhAn|Hc=owr}TyA}O!XN|kW<^&kV z<(wY~IO~iaBf05jM@Q@2miJ=bK7Z1Cc3(y&FPxj3K%{|YB=@iD<-%r^X;PaU{>nzf z?ohLQsq`nZ19OxSckT3h$Bp6QsVF5-^~!qs{AhQb>}jS1U&k9fZEp1JY_dD7wbeGW zljm@0f}qD?41k;^2jM(*txO$ncrJ+AU%+F_-AAwtEI|d0zDb^SuqxST=|Ls zpwXxQ`6_p3qR4znzr_mZx$%neN@a(P>#8!tsJCxrp{^m30%m(%bPsIripL_$Y#$&} z<9G~p3CkGXh;xGRgLy-b$;Ub5Zi9Nk7hoZgV>JEGjm-9TX}I1%`)j zD_|ZGbJ+o1_#k%MgZF z3ieV?bZY><%n*acJ}gr=-hZre%%AXBf`3V9#gYEpAoR|DcVR??0}}Tv*lDXFI226~ zbBBPwIZaX78Yj3GdygX*hpb1Reb7%VTJ$XtwRtdcfh5|whfA|IG-qB%-pti4NZOo| zuhJQ!7m)QcvQ5=Y-PWI<5SNql_m$XOA7o4On0cpn&wkyccWIdyX+3tuJu{g0`=UCH z*k`>2P=MHR9tkM43heft>yewo)%j;1W#p}~PMN=#)~no2drde0@hzz`=aqaWhf6#> zJn04~y(W3qCvqrn{mzXl4+&;l&Ta_hQH-Lg$aGsus`bYS6;FrKp))?z9`28io{tlQ3i{5d%^L=P zCUqCDFXH;+6{RO#Ge};(@Fj?@Vy*E+#?@Z&&9ZgJF{c64-kz0pADVCImhhy%Zlq|Y z=5wLMvH~}P?a`V72gb2V@q*Iw>%5HnP|WqooAZMF`%PPX>t4yBm7vuhIUfwrY^(_I ze}1-i`{4jFjr^N8aLtDrP^CqpXY7rB(zd+(3zr-exy=sO+TKW_wI6pU8dEi*a$B`h z-d4ary#~JCx8__hA8^t^ufZKuO|yUrm4fJs1_y4cAMkk1GMExnl@^%9zehE~^6b;C zPX?k7c0n%zubmz`dfLGteSVfB66q9tYLFxzQ9<{`7dJ-C7thlzIC`W99rPGE>6lQv zTz6+-P$an8TJ@@kE;)I^I+GBx1rs&}pl4$7{k^@)Xcu%lCr7>M3co`@Cz(k3o4`_gv z8-ynmY#!()Gni9vjbC*dFVi=uaQKkt_i9`xU6Jy70#w4cC1Adxp?m&L!|B2AWPu~M zS*qY1a?QA=EGK%YYx-Fu7=4zaF5x_`wvP%=L?P+uuT&iT;wkMtCfT#I)Nf1Q3}1V- zto8K}c_g@(Y+4it? zf}rX6sG3W{T%Iw2F^RDfdRujk684hM2dt$3HUuK` zTL0lFdDh$XaBu3qTl4pdo-!#uyrqCLDb!!ykl9?`OHDN?_V*jd&`>efX6W#|y3}__ z@y)m)r@~zO-h{RI%F>dRIOgo_Gl46u$gcYJZt0Z(Z|`%~X}(;Y#MJTC#`y0|g6}FE zM}~7WZhz|6u;19o8gR2yb^-DW| zKQb0WoNhL$f3Q}!R;CQj4=0z3z|{%2b@je)yHn4#JZOZw9+K1BVUDHNi}#|FBNEFr zIKo6qbxHVO0mB5AI7Mt|iJj&H0^6!RW33aeF`Z9*u}}hXq5s&YfR59p^lYuCHPK6V z63zQ}He?{Y+YZ-0t2nE#PA#YNjh&7g3kz|DZ0oDCCHPZ4(m}{5?x()4jZph#+;(oy z=DxXMw=;THR=>P=R}&@WPW?dh*&{aT`fxKoo_ppboY;}q2l#--H$%ng4#)afX zuMM=BpQ3O6QxXx&1czih8DfVsAhv0KmlU1VnNV`Wo{4`|r~amHmfSA>85deU=R3fm z#%btrUsZZe_mLS|)CD^M)WTd1~j#?*Qjk;)!szBt{8@d~DG^E=2t_(sd^&;}$MaoVOg z$PBSG$MQIL<{%1)RH-(qhF43#%T0KZzOhDEuhxjrYKMFAVf(gqJ zBYYfs&Eb^}V;>Q>UWDA-^moRranwa1*_is&CpWcnG^Xn78Zi|g0tt1uCO{}v9x<9b zp7=GMo$LGST!kRe5bG3n_ z_v=tDRmgq*tG*K|3-<9?@=?LI*S0B5b}+g~u=CAxY)A$b za$Ku5-(9;kNHqVf2{BlbImYiw$#+WpvsQnT(jcS0lF35EaM^?r{}$wd_;SRk)w>cZ zW^+QG8}rJ`%Q$PtBT@ne{)V_i^ADu!INtL6g0>2v$5-4T(QCfrL#Cah)uvM1lW_BX zLj}TIuk4RQY2MY{DXhol_F_<(pBTr+)0{Pc~X}w#-ef%BXJ#X{}!GaCIX?jG*)J!r;j$)GTY6u5BY?5K0Omyw!gYS7;7hg*pT24yy<-8n%> z=@lx|uhx@mrB`WLiVJzx&9xYik9xgVvDz5MA(>Yo%KRg9ANQD2JVz?iEimYfNWqE8 zV!mjPp){&&tKBTeP>Q7=kzbEx%yz`l9j3ku705_x*(^&xeu=s)Hs;5O*DX;4xK!M0 zQ_=!xzu}xfM@Pri4`oV}Gyhl`5ZWHel0qJ)zgKvsdu<7;&(1qkV2CTkl&dm%%lRoy zgsH|q3yB&^@~mt8HX_c?ABQo(nZr#z^}Vj*GDf=(ldbPUvmzLw)mMUTLBH6Nu#g42H=xv(+U+W{>&(n7;Vl2LC-x3MERNWC;ahRmU{YC#Xe=RfJ!H*nLQ6|> zVfUF;#vV@MR`1e4pryaTgS{Qg2TpTldlyXwHx${}n%UqcH+7`_?k}g=)Bc18#9mA) zm#(pSvsF4lGqd!um_qfPEn-`O_c!`q`uQVs?O_rvQSMIbGZIE{EmEu|m#UrJ=jy?! zAU+K9M)UBnt8)#vP94MnOfWKmLh_HZ%k&ts39XQ8-pl$I`m5e^zAXDBi~GyY<{~c` zHlF1DR=Oy7FiCx;gI6HBih9idPg?er`%uZSQxY)-CK%Gy~ZLp@OS; zSCRUcGJOW9;rfD*`E)BI@7>T;u@8zK#fgu^rgG`c(Cz^F=AW&@v!49vzQ<`cKDIGs z^3JBkJ358IP0`a~NVz4)9GrQj1#N}hn@rjs-_As?gSoVu^V>h>CT}}}Xnd!waE<`o zlRWE_d#h@uGa=f>1j%5LN0l@p=XaszD@r~r)Yfz2+Erx*`NNgtZthXiaWQAQlZ?lV z<|g@Bktr&Uwq_-ct=D&|dr@DNVv%|xtoB?^&gsPGOsO39k17L;w#hB$)L{(!UQd$a z8FuVADb;D1GtP2cptBQ*_tL6z>zlZ<9g{`gN`qz_Q>{^|j0{^#@VQHZ=C4hZNkg?r zyIvH+>9TGvZhO~$)>U0)#l*S5bjFG-3a0vwG{N zNO_@UX=@m*TBH_z*8E_o`SW-q^ zgKDiBkNG96Ndo3f3XLg(SXfIih*elP+&ksHK_lxI93mkC$8-DDw8R$Izil^RH(p>_ z&t8LT7ciL(5ViyC&J=)TS#-Hq-2U}n+HAFy)LFUEc%R0 zKj~kte@5Wflx>D<^<2On%NOmU5cPv!fAh#9S&cKso%yXyQdhMT%?Nq$D!O65oO-QR zgyG8AT_*We$7{`xOBE+as zUWep0N`<|`iJZeQ&xGY8{D>;Wkg~R2Xq^G$(b=AYU|bDF{AR6IV$g=@gf`xe)*`Ih zzK1$?m6S7f%lg8Jh4c*WmETenz>fmsB&yIH9XxmFtI zokko=EH(E=_4Pt?91nQnG*YtEv1qMv3rYP-r%<(IxiQB~ZbEEqa&x&Xwk_4dzBi*9 z=IZAk`#hsUlDSON3Yj`JKC)hllJ^dL--fy`Tr2zWFX}N056|=*uOsgeTwhExowq)C zoHv>C8;B#@0Q+xjz1&OQpwh{5XO1`N(QwfQzQ2{O2W{yb#UYqrDrc0$#a$chiLG!< zpktVD(_<5L2?Ee(QJX@*5S>)6LqGvwGc460xjjA$HZ_&(b5a8U~jnL9&6ki zK@CPls4k4_5aID-Qsb8Eh{P2+q^eo@9(o;v)4m%TQSPxY@UWzlu;|1WmKY*@(~Nou zKGWZx)*eDL;hU%S#u4JaJ8x_jepbwEIR0^BCw}dE>Q*}7&Kcr0#7-GT(ApH;2(A)A zuDav8jhEe0=Evd5?z{O9F0O^VeW@grx-`nT%v-DA20yPbk>htUNKW*Kadd9Q@Khb) zL+kduh?NE1Gm3Sa`WpEvRs6~j{eSCo^KTKc&w#yse|_-dXT5S&2r;Ov8OllXzmB^1``&Rt)#@SjX<<4Ps@x+zE7xdo`X<=N)?8Smry(AzX$C-S*HL_DGtR?mETFfy9-$v~mb^wRxA&t89F`#TTn zCzyAr2M<`$H=rvF43dK8_qZODoY#e6oQ}vB1ymR&*tsKOH=qciTf+vTbEIflhAToM zKDC~&gXX5bgvXea$zHQvzkyFL4U6W(H^8)=PD5wJR2inq z*8FY|;S$3r+zBt`O|~y&pGqyWx7kzvjdPvrrkE(P!kVM50Tt!__9;*475ue{th;LQ z=SQDAw&)nf9{Cd&7~j4%)NH;IFK`t&bl+@CEf>*7hiw*L6&RGqb%vSFrk>4pENpNd zDa+j@RpTsCsqk)d;__Ky#f#;j_rFrI{+R+%sS2VW?RU>ELaZv?y^%7u$;cZR11L!y zVtS@{*18;Aq;u7t!S-ZRG|WsC?*75*;~)rc8f*>{%be2bKrg?-6ucCwKU)>Sgmi;} z{s769POd0x`!MU1lAZU5zYkT@8rgddY09VY`Q@}(r$M^u90qD_0ecf^tSG{95*SyOz`C_xWoq3uQZ9-eb6;6ZXKpLy9X}N1clu@Jod35dD5hjam{%ly2&;s{ zp(yA>^UiTXnaVR#tW|i^=oHDZb_GGMFZh#*M1!mDu+nerxXt(5fZtlZLRV{)L%2za zurFMd?e@p+ApId=K->Z?mp^u43uH%~qQ-c-{d-q3E|O)PyGI2ryJ6KCg*!b8ONSU< znun&8qozN$yY4f0CTfF6%XbzcbW3xpZ;XKRhHTO+hAjXtiv0F#d9K(-!AKr>y)u!)cKoFFCp^_vg2}(63>3_M^t)r zL=KN^onPd zaENg-uC2j~8J6?wnYdQ(M|U1lK0Ta(l$M#CjjiSL3sj)&fuw`+>{vZ|bIJFk@wA4f zG+xv@u$6#c4sf#Smc_yNxNStWK?CWy+v~~M1?x5d(O9m8tLf*1CUBm5lZa57K+#2 zg?(lK*s^Z%DNoQ|32FVo^4tMumlBB}3DrB=-;sU+b1oJ^MW+6SIgNv{w;!_&BS}h6k0*7R{7`l)CBe3bQ@Xrxz(kH;0F~B*irj*^T7;E2^ctH zk#UcDR-=N|Vtb7PW*)(QOzg>9iMMa@+~|#Aq1bJ~Qoyw7?{0zVN?=Ct&i=|70017n zo;3V_*n7{gCbz7ASZRugjV2vMdXwIysenpT5D}?L@0~~s#iJ;ofb`x$dM7}riuBNX zAoNb?JwW~&=b4#sKD}t01S3u1XzE(9H{aVGft` zwj?^5M$0uPJh%jdUX8i#tumD&;;j13(Und`ecCTmPDRL+>kdTc#2G71)2$H&xlt>g ze3~t#FZGZw##w%F)4gJ#%-l;)KZF`@dpMU>NdKT-u!h-WL|=DXpOBe@^yIG6eoEot z>k_|UVe71Xez!~|JtO>YQ&_aLZ%7MIIUdl_SUTgmBF>#h6jYmevfkC?R~*d0d8z6P zs`QCbJK9Xq8GS7OuE^O&BuG?0S~!Q)_E5xlwQ+r7^m0Y2k+fLfmGY>P6byU&{PN!W zvh?9P9XeoPLk>+`uLL4c)vm1#FjTmp#r>$a8H#c28lP#dPo{YP zG0jp#7X0%S^@58YqTT9Lt)CNQB0MSbdb5VPWjiiWOBDvx(r97;g{ZzdgS4S&{>l6{ z|3k|Jt&S?t(bv#*;kq696#2F(t3DFzw!y@rmA`8sk&KygX}Pg)jv7>+__i8GpnH{{ zGRA>j1~C(^MChstH|w&#CyT4o6ir0C?eT3kraWcy6cW3Ul|{czL zoyhL%pS%R>V7`=CAilChVPz+JT=-tug=;)xEML z)OFNEuMtTU`a%R^*tM6mw^MMek|3#8bF{>P>5Ez_oCZ#ct0p!;SdN+G#D1$IqufiAJcC`Y zSWeci^D7gx4yp>XVbH>PIcv3cU?|xOJEb+Hz`luX;;5*Ag&2wBavMM)SRPOGh zBl8SwNM3XrO0?wl7+Y>{ z5%rh(Mu~JpN1SYkf$3Lb;*KxepO|A7$s(>s0R5#7LTxQzinAhRTZdrJs*gIAucD64 z_3n7UQ~79VFn=EJS$a&(j{8cZaG`P|UVw$PgNEn4WG@al~C0xNG;35SnX^Qz4l2k)YUGfbyf%{yJj@ z0;IK3i5;!X50<1MTJ52V-r&Rzr|cKJ>A3xEh~oO&5Co7{7{{A+g<5=^8iot^=YkC( zwiRgLDy7fDMlGv!;LhQ?5^ggk(Qxf|`a7@NfPImY{w_p0sF)z$`~wCElRN|TRI8ZC z#K|?omal264z-lkH>VzvS9mJIlx>;fv1FSg5j)5%Fk$=R&7c zzB6vX)kuTxRLJ-qwf)05Vb)=c*oq)T1Ch6|{QBJ}pzxD+KObiSxSO^`G*TYV1o~S zD(Z)$Frb0>U7>CTLPHT3yKWczk$9oz*^`oU`L>^+F2fZhTtUv9n+j_03JfHw3?7S; z>gHP^yhi&aWWHuUgc_I`ueMnD*ke~NyDVg19;U7__yWr&5voPf)lE^7i}sxTv^@~mGz#hs?MBb(8^5-<@5&BY-yDD)*y3Fh!m=ra|ez%gTg4F)ldx)|#U0V6{aiR2QUSG;r7WbgD;V`FeU46F%$s z#Jwi%&1!n6uuH9p4jIbE^qt`+#sR}jE2rvFaF=5=htyB%s;VXj&aGuN+;yuB<97pM zh|nB^LEmv5L$s^wQSW8st8HO1kGN?rX9?o3Vcie`TT0V4PpHQW}1 zu!ZR(-OjPAZ*r$|Rjhj&c0QJEmi4ZTl&y@A(G?H4jK@%1t$sv6OmodHLVIUnpfp?C zVaF8t-hFe?5K_Zn+&3A(uN>ISaI(RY;HPV8=-*)x$K}e~MVSGE0JTcx@wWbU9e19p z33p17#M)j(z1j)~#$qevP82zhH(wf`r(1`-t@Ab=*0G4+&4aPY6;x0_9CqQWuK19#aTJpe;ALU%6V9?LZi5^?s71Z@$=gxEL7($ zQcmxdtiZy1H&+IYe^oO<{X_)F>r|Oq*PE&g^<}B6jg(kVkER#KiV}VsP65p6dEL@A zS~oHW)fV!#+*?+0kR^$Vg_GFFCqnl|_z= zdmZLx%pCb#0G?J<476oTHVC9DiP{yDTe`BmfgSDf%5#f!bDM(1_%_{=x4|tmRpW+= z=ovh1x4}S2jYfXFvFC&H(^^XF)#O=hndOm33cF03K%pXoF z6#yVJZn)#ic&YsEciqwpDw2fwMN1}~S_1y=dLh+f@UN!aCF|iNuP4w_t~Gfq(|h$L z>l^Y)o;%sd%@(5`!UtZFut_&UCJPoSU%bhbBpRXF0T z1s_zL3}hLBo6zwb3v>bH%uO7G$F0XITibbzMbQ9#RUN+C+D@wA?KhX+S*Df)o{^!05WdkZspry-g=a!?_CZ+_c+(?+_VDl5x2Ej4PB>!R|Wb>{B&z>7AYdtJ83USisO!Yzo? z)#`CUwr?=fC$?qQcO8l=ZCEPt+G}fln6^9aRN91o*3+H%L&iF4Rr}x)e1+RY)K;%1CP-DV%t^mnq9kUR1 zIA;#!a!s}doukvXr0ZsTeAS+nG}B)+S-t8WlYH!0g*#}7M83KZrtd>-0!r%ljAPC% zyp+XF)yO9_66zOHzc-v}^)z-MpZ9#=aF|oP10qafSlNlzMoyI&+RGrMC2(;oBaFjn zH&%4>Q21Ff~FWR8OkGbf$;XWg@VooiOAdapwnUq zVRT=2g2%3APvHaKa_3atYFBG<;;ez1qt;FO`%$A#nr-BW<7g2lB69b6ZLdTXAneMX zBBw*fPvYUoB96enaahrHZfynCOGRV;&ISlVf;@kf2Q`oX`f01-tZun!A^$D_lRn)R z&Uk5be7J6-+n+VZ&4A4BoI^xc^aHS<^L#b`sp>fzMl_nm{FxfQC6-d(yp(q}rc^3f zW{w20p5tBdYI_I4wl3lzX!3RR&aj6V7P&Thun@II&TXF^EvV!??<5zv}$x^req%4p&PQR2G_|re49DQV5Rh#l6kMznw-a^ZF>$OXhu$qCzd1WcjSC^)W z=N%goLfE?oss}hd>)TQKmT`NMgS4C_6PEJ)B|MWJ;@%CoUhd@dN`QU(@+@|cQLZotL2#l(Ww zR=3#H#?JkiVOAPdOdCmm9Aeuw1sjK2@P*`+Wy>kTtjC!7joTfUqwlbQd)-^DCCu_x zX&$qKj$!iZqrNBZad^3VqGh27TX&vhf$EC1VnZ)PrXGu{I$OXgfvYikXm9pa1=kM$@ zqNUZffD5^?31d3Pi=#UU$|voay_|4^#(;3Yc+U0fpFKdKesCbSzjM$VH%a;_tfzg! z#3=fpeF$h35{bvmo5|cqnC)aA-bS}s?OWZ7KzdQ`2+xpr0NGj;&(KgUS=oS&{3&=5|li;g7 z{k)TwC~XRol2L2>I6dFYvD@F~R96gEse@b>i{`C?9zWe=wMs;Pp+k(w%gKEp!L!z* z9y3{^y$*2q(>13ua{;|bII=e?T`6R2E!suT6NCWk{v}S?T&r};u@9)~`fyjL9~5Q_ z%bT+idIvR-t3aMU!TpA$q<|D!0EyrGhVyR0rw^vablFw)rU#=LVQezpO-)i4h=fIu zW4?)?J)0o@wRb6Q3VxG4!`g3ZQ<@M3OJ4BcYYctMC)6r*os}Dh>O?!uvW{39>Zto+ z+l(|wjO<}$e{b6DcVBTgf8=>_BFRD@%uTh|wkt*C8EYb=&pW_5gXx_w%(f{_VsDjy zbJ?%_$ZvijHRphoj(6E=gg(?W>fD+nF^+xofpD~O|B11}Q)8RU`N%YD)0mDAh=j^X zqC>-zfkYa9>U-5Mq@&fzGBwV@f^PQRVHme2{zwpMvA&P>h`ObNxG7*O{!_aPuCLWa z38TIGKZ}Z|tHi7bPNJ5s=%AdnfZexv@dpAZ#t-UsLR2^_h$Z-GJ47CAKSY4DVr&R2 zPxVn&thMqR0X~*@yJ+N^Ax9zX+*VE_=P5jB0mrre(+Pt#bISb;lXw3tZ~xDur|6*LmovN-`?mhT+BP>AfG+O8zm)8KP;C6Z?D(|X;4&XK zkGDoADAPaQj(s6eT)}lXA{PPjNxz!1XjvWHO+EJwCdzxJL4@C|O*=FqzAD8C$4Isp zw&JaucN+lqkWMnZW#a&7jm~^7oQ`#-TFI8#2h(V*r6HGMU}!p7NW@~DVP4zCF(Xdg z$$lJnnnf}6)EiT@V;S?(f80)Y89??dp27$oX1|BhR~@x-beJUPiPq5ZdJj-5l5g|W zM$bdJ1s19c58~Iui#w?9 z&>KAZ_aA^`M^IdyK1u4)YHJroTl^2115(dXP8;dm}T4U-1WE%dv!Th+Ax$k*YNdV$1Nju8=|5v?|Q1n>{8DBKpJSxKzUsG}UB{ zO!4`yAm&m4vXn824wUl`0T%B#<>b-C-VY09j{=&1a!~*0(UWkmc%Con0(I*XNe_}W zGfUl#a2B?&>j;!v0)2JmyTy>G`23r8vt-J0YHn9v;eM;8w5di5b{$S7Q5+ClH}|>j zu}?PUdwQb63s!Nw@zt~|h1fm7IzOsN@UBE95Agu}Af0+P4)|{XTMN)dBL=oE4}d#O zm)>FY)U*uBy;p8&CT zEZw>%MqO0HvJ-DFHJx!ukJ$F%Lui-u*xop{4wi7*R;Sj_ z|GZNwVcjuqZd<(jx;RN5@*H@7gwT3~n(EtFEA*zfFkf^jz*KhdMOE*B(NciZbsqZ- zWJFnZ^rpW@Q6IV`jN3qWMf7TQMIMyEYFEB=Q(Zjv*VE<#1!-GuuWo;R=4wn0#0he8 znM7Z5JDI3Bc5Q%RjXJOPMWf2ww(20}0gPso>tey{Rh`?T9sDLm7!bw zi9g-W2dhmUq~v?X2<1u$CTSiMzX)k`U!Rm$eh)p_p48Y*PQ9+7yQ&>K1BhTgdzWCP{s6^emVn!7dY(A5ljL3s+DJt2~Y-1?#J zGU>A*1n5$T9S!`8R(TCpAOEzq97 z8(5dg+T}UHAX@!^WpSv&zr-3T*_PrEVQoLSw(4922OQG)=gAtnZium(_y!)c*%}PJ z?XjMxfu%Nl?$-dxfI*6D+JQy!{SF-~MXA7c$(x^dSi0Onw}83V>K&(*Bd3bPVKN~Zvv0^7VfEl)lTc`vLh33-QaybEh*PLX?DKoKl zuP84CIY?wCunZf2xpSN|3h~4d)-cl^uEa`z9SFs+M;<~f)Y;V8JfxbE`d^b1lM(Wf2_*OI9ziF#AKa(!b zTpf#9GSolyWIphWANBY_BM77K-6;hP1JO>FE(aP`;%m+Cr0?E5geX;IAC|(LoU=TJ zUE)T32Lh=);*?Y6cD}m6vw*+4z4fQX6~il@BQP7DY1CUKaG(!=&_+_cU)Yoolo1bY zTs*S5idB~jq;h&A6zZ!BjMQ1ME|Z=e(O8#NO^2Rxk36>uOuUo`jJ%AaBU0$6;d0AK zYOyiAa`%urR{i8;fw3d-lk>5ft9i`@B{4hR+K-GD*HAQaDgj4cWIGmsinR4ndFHzZl|-A#9>1DQF7eRtt%OBotz2xN~` zQKLG!t~*q!aAz*~5QJ*XH(sH4s|CPj(%z`bk{D`8yg}B)%_OOIx&dfC67LXSal$M& z(z{^TDHp4%Mv*?!)_B{;HK!!~xUaVlwfu}%bFEN;!$#`}C2WP2 z+tI!q%ez8l&{#%Atv!tJ;p>7E0-n8&s&j@2gdj(?)KB2o^<_`UQ(htC$VI02y{7{+Oqh;hg7L z+&g_VV5Q~Y6x*3wDp9sNRM^CTm z-D2eo;q#BvBxL`@WXCEi%HOb>D;Twgwrt#S?_&ov zBJUdv>w_1tJa2fM*HB#9&=XC`gBi69D9}UZdo#mIiJcSKGSHd4U^Z@tu|ZfFV;;R( ze8`d_*PySm83LT6qP4zS+sY-eR1qZ~&FzZMzT0D-U2JOf8G_oaGVXV!+@9P0=8ajs z?!eRCl@}`9`#I*$eu1X3a_j|^yHfk`)>KtY!0#F?-tI&o0oGNv7~s@9Hmxfo5MQ2t zNG`F4j=&ceM)uWpjxe{e;d@tITEJ9)A)M=%f5u8hnl&6mR7@J`ZJcUt=rgGe zW=KfLR`p}z6@-PY%{}VD!54up2NJ7f4R(?l8zTl5(7^mdj3IPSIHPb0qrtB|FWgpY z+`ZeD8rQq4se2DU&zw5Qx2BI_VgF73>XL5x`7Zin8sF>>-cdxPdFEQ>1`cvU-)fsh zLKIoPyf!wQB2E>8c(Vmfq4_(mi~&iHr^?v=Jr~c6HpMWoZ310CHwR20nHMn{hjFWvE7px_am8-{DlAo1sfo-E!tIEaE=T+>{97Uan@z)^h;| zXBj+`n7WmTssk4)pS&5v&bj-w_b_BIGQuuy>-?M3vJA@J%1$_669W39WyGb7z6S=w zs&5Y6VK5S%n(+<&hn%VR8=fu!QO$Vh(ql^dj*5;N#YmB7bmiE;lfMJePvCnUb*KFV zHqyQEwkgd(O5ghPx2s&P3eT#QjCI@8fE6%6^M-KiRL+zVL8=8DmOJ{T^22e>E~LtC z&2?B&jJ^EV@+^McI@~R>I~@yJrZO2aQ;w;=i?C+cg-T)wj#ae5u1~I9u@r_ z{;I<3GDu>*KdYc#Du4UrM&;!<*sNn8Rg2~py;@@2^8 zMpJ^wmQ=p>EeLi}*JW?aR)2*E>!RBszDEP=O@O{)!yXM9@EI*VhjnyZrs>jKJvm-W z1=x_X);Q>Tz3;DMT4z%rH3#rvvX2;!0#y7Ksgnbq@fw?|N!Noh9Z2KlzAMpM@apYN zj?GbrPH~y(GL^V8)9(9ubo^op;TsAcbmk)8B2T6&m&Y>pjyH5Gz`1H{exqab1UcRu zq9TD7z@lj(E%PB#EnDat8A>tSS0#>wHH)3C8s;pbks`W|-S6$O9-gGtao=D833p{q z-j3rc_q&Cl!qu4zFrQcwLQe%l5MquZ58~aActz1Ay@o*gWV37hWUl6_C8O2WO0UXe zU{YnN-tZi^lHPOb@u^ka?WLU-e2Q3}J(L3scshx+71408Gk@AfbMbkke>Cpf4VA8~ zx$RUQR_ObO=6{e4YnDs&IiO7QoW_f)K;fxJVf~a^XtIIW(_|{JCX^K`q0p?_P9?sj z_ctXfWy*;y$G}e;eO&uosS^%)yR@Csyp}I z%&7BXPR9LtTH>B$J4icz63O)y>@VvqIA_ z^r-kM!1cF|?H%2^Euw|w(kyz;-RYkDLC0A$e8Lhr=XagaXe*qn%mR|7IcQU}BQy>i zL$+I35?a+$^2re;@!4;RA}*{|Y5Bmg+^5$@a)cab5}6YVc3O7m=lc8?xWr#F6n$hj zyP0K{qK@wV%w33dT490X5nQ>}GN)Y0;^xi^vuRujkTAw24}Itzg8jtyDQkrU=i;N= z@)Riie-6PCzlD zE`71){mxgU9(j7y30FFqVRYI2)D-7b!jGst-bJoG+|E8f;aRl?Jy^kb~o8T1rK)IX*bjaqcQ%Vvuh29-+>U<)^-ZG=NmJK~A4+DkB|FE>A>p0dUYSL1*lXD4K?iLBknov>wQmlLaWForz2CG{7x63R| ze^abeQ>ia&^Hf1l`nUg;&NV4sY%!Qq*hNl7VPGy~x;m@&|CuU<=VGAcSFaVcP z03n*N;^ae2Y#wwADQ?)26Qx>gj3}>!t{v~iIaAH#9$yw+=7dPlLRhAIQZ#HblyTDp zuZ`s|9NH++tgU7fr~#^rdZqK)q|h4R>Qo)JDyZtdOHncdoQM+h;qQW4C8oj17r>?t ziyO5+fK=A;C+9Mp+P729RAriXIW5=^F-0^B4(hT{-IMe@f$>4VKdb;LQpY9@HuBs` z9plwcG*a9>M*_iwN$AMF`P&gXx4ln1WQDAIgzdlR{K>!iPoUzzSM}B}jSjHM#A^p} zQ_5_Khn|qC_f3_=@#5dPbuAPrMiw_=IcU4^{&q;a%}W`qII0q@ysKDu^dZwA4Zu*7 z^a@m3bWAdS+aNBf=_e899fQzP0sLkgwa4Nqk5*n7cbJA7*G^a>{EkmUW3E6QW~1q3 zk-!8g8JpAX>G=?tPAxx2MDnWbt_&McG{w>hdBaGUYi2nIt)vc{e`q#pz^rv#+AVv@ z5!_a7ED2C^li9smI0qoJb9Z?eAR2NtlLZssqDP#Zy9wjWpYEt@e0D5L`fwV5yH|QQ zLZu{ZYb{^GPOR)hVH>-4Q*(%y?Ve&n zv?*Uoo5SRYm6$W?lVr#%>W!{EI`0%9_M(-IE$Ho&RYCFJJcVrUK-JQE5piR1hy;|cS}_=fmajB<RgQ6wJFiDJvh1FV6JRSO`)_RmiOk60c~ESkh4%Jhh1Pu7S!6)Nm!-;|vy zqpTI_Fb^Ta0os39bKGqC#pEm;g5I^7&K)f@$iQp^X{q2Sev`^h0|ciwX-9Jv0xtvE zlcT97u4R}G93VEg)l{09esDW9ItI2R=SEc-;0w83$Qv%3_r&D3$Ly85!}Yx_k!3o& zsj1C*W1{d8s3o~2X4D4guxQqrv#GN$=WeOC?^<=+Ij&C8_vKryhyWEe1`rA7~`whhI*YDG@B+=Zd!q=YN(4b7D`9Do%MC zdv4)L*h9&qmp&lAa7l=&;2O1lyj&gBKFs1<`^YNn9QyW-{oR{qLQ10HrM_$pjaOx+ zY!47EACo9_45ze@aqS#NFwaS}EffIQ1)Z~25^v%Hh~75THnXU2LPFmLk<6}8umr99 zf#Y_p3-Lql6c$}S{l(GDkWp|Bp9N{By&MEX3zYUCs8;9H3s-1@;7WJ*nt+y^75uyI zH}3(|jn3sXG$eA6?ZupvjpuLP4*$$Nem#1Bt1C-RI$kV0^vFoL(y~7$jNI9_Y}Ig1 zXqoBLQJT#_gXsIopm{3+bJoo^t%thQ98-_iL*`F5i{AjH=fQvum8&^gK0u0^TZV@j z?sH~J##){yZa6VUX|Pz{1d@?X7*x4kiujsvu~{!OtkS&HeAPWC3KTEP{RN7*xnQF+ zl7aD}=D>!7!P17@miVS#sd-pP?g2mpt0vulztHd3u%(Axis1WUctH<%rf_PPJUTfZ z-RjA+LPi>3`fZ>brvQm?3-VP^*9|=tlj+Y^hv39|p6~d#uwl14Y$Lnye*FW5*y=zT7qM$$Fg)@GImRt|8 z%>36Znwrl<4{tT)#21L@RmuGQMHUFk#`ve-`sPTf`GMf4Z`q5B@Yg>+@Y&XReVi4Q z$kfY^CrQjjLr+{vW9=FjMRV;{!&$(7tfq308_E>EB12}rw|ZsEDi!7pnT%fAZ{ zyev{IQaq<}H;}CiH|o|;XQ#O6nrL-}AUy~h6WJ3aG1+(~3By=+Mzb6J6Wiq%F*mEE zr$_n8et5);Uo;we$_u;3U1J+?^v6Zg@itoJix=X46P+P7@^(glkBYC|e-j>(J7QA> z9CrA64L_AJ#p^EuD8TR>-(FjV{X*BRJ(unFIW8^3-Z?}OK=MJJjn{aL9sozHy12l2 zm&MtL+1BeCE$bY4p{HnW9_NHLrLpA>ifu)Z$;<@TO3XNCYnB|h0wJ%_rPKTufbM}~ zw$U(O$fv|01+WvRM#Ss$8x5|GF^incM=}n9O6Zm=0QhqmQB`YJ1o3^$X z%&@n|&4kDzX!w*p;B7?k+_%v*en{F1=54ZF)?t>y5uLmhRb2TJGhnq)_T;{;vndNg zk2sXht^!Z)bC4H-N=zJL)C%?(Y8+dmm@opfegKoTb%s7vSgph}jTjvampJRH^(0mr z*kjLbPX35HexjfdqIY~K{53wFt}x;72nPJ!32gLi>kmNaoBb#RRVi7UjL@0m9I14Q zIXV=EnJj8i)a>kv_Y`Z;FZRRp?`_PeS!;l1VxBm5N`a?srX``G6WtC7+bvj${h)Tt zp~Wy*)&k~zUEKERkIC4}JLeIY#yF|#{D6?f_KtwKy=?gdF;#h+AcI7u={1bU9+s`L zvMXNCdGRhpYf#toohgH}DScVh2_8>qUCU68H%!k%P=&i_-= z-Ax~esSQU;7VUq07H&J3PnZud0`oz+;P5Mx!to2PP_dok!{vj<9a@us2yHR#54w&? zj%z3-z8-$V6WBP&=&((+Z8p$j4oE!ZnUMBd3Y}Pf+8$zH9Qi{|+`;3+QGu3Wn|C6O z2)n(21S_SGwYY=t4aHHXDAn_sFXd99KfOwFZ!tHEZ$dJsiq`OXJL>Ar6V%;PB;_d|2;gz(OzM8{-2C7%rG&r5HhcS#oe>UG*L<7}KdGX&nS z`X@pA*CW3G>9i+dGdqL2!ATL9Nf3^?=f6W`z6)CP1r-|TMSzA)ql7b}L}GVmaK?v- z$DYsDA`wQs`(DppUE=qJV4&} zpLZ2Kef6k+n3z~Lj2k4;F414L|S1ZuX zY~IP7{*{#Zwv$*eRX>NA+6ifF9Z=Ye4d}gaWhKIojN@^f7(m^`8H5dv-{jbnT4ymS zBv{>D>=l2ZChERlaGJpO^jvvF7V-%Wa9lTN>u4;DJdVLB&#p*3f>)*i_!b8@H0;|L zhQ-HQm&MFg1qu|yjQu5tWhDmk$Mt>MNYb9A$koWqu{Z>n12E*3;bNAbE>}VG?9ix< ze8uH`NQI{*_3!&5$*ltMS+8Dnc_ELun#6_I^sgo*SYFSg`!sKD1?t5?T(;ec^k#o%5_>Jc<7%sk^FgR{wMDIHq7VoHaP(46pF7&fA*Ecmly9aF+Bzf z8zna5x_QSx`@!;h{qxP}(9iugx3;vs&ske{Lc=2ki2)AKgH=n+bS2z z{%;&rt|ds{cSo51%MIKhXAptEt%U~sCC%v{7X~1vKo~1R!_V%w{Omt&QGY*VNC#7Z zTi9It@bb0){fGbKJO44-e*WN0`g1nyP3+%4@a%~EebxT^SD(%R4;AiBp6j^(tZV<{ zqko$K|N5u*)NOEY?Of7svYefM|J@~!x}0YCM+yz+e&2`xn@a$Z70dx`UAD$g|J~P} zy&JN+z(wOW_6D!?e=~`H|Lx$GfBI>Z7(jCNmL)D+T#>x<)%D>W^vkDTj6UMMlQppn zz&rb<(|ag#9mm`2ky!{1SEi$MRw!BKvy+FG?krN*&){mn#?z&bcFL3yeS44LGdtPThu^m#Ft6_l>nET6 zSb(LhK36|Hj4AjfQhjDRE9;!EPSoj)dwS-_f~zgSG3+Y4@uw#6uQ#~kJJWA=#!48V zuOAp^H-9L>6Q#SnpQrF|7%@O`@CL4oS#0PnlK~RSz7op@YqKEd_YGcstn|+izwKh) zYR|#UP}_cQ0y}d%yx-!N(Oi@h7>f(%#_HH>574lZJ?s4KKc9|m;vJ#|{3TaOLj*f7 zMJ|1CjM$wgpHtTlu1ol;rre__jF{OwhdC2bFE*U+kyWZCXNOr?<~$-EWVIkT6OHH<)n3Z%>)m^~|D}z#~kh`?T`5afDGh zot3x)di>qq-g?U(&qV&lzq^WWUb=~S^Y^!7KgLscTaLKqHE1PwroD~eJ&Va<2x1GO z{IT4)9WEWI#w{(*%j<~0)oswEFwxe+eRl80=Lj<9d*IS0to5K%z(Gi}xSH=@Yc_S6 zw-e_dZjXK>cgt&7b=$5W)$06hnlW(d8@#nrw0ZluB}#l2r$ixwn~BQKbLC|vjfA;R z38Mk=lMary__LE1pTU-su_%PtED8I0Wjxh#1}FaP)&1IAGlIM%gx_?A4e10*M!Z@6 zT_E=6d6MGnLO9ir?7}LlUM*FHH;=~(S9Y_tD|&z=5$`F7aZ8iYD5%!d%iqk zxF)ujqE_nyr+1F>$WyBoTiaYX7%dCbO_=|4P z{yfi`+4cE#OmwF7rUy|2!&+h)e{L;T1 z8}Hj(V0{NeU2f9+U%s7`o@(Ik@^2``Ty6q{HZ3 zYx`{!n;{hudCaaXzBzzg6FE7gI@+K1yDjYKp|>!!owP>rm&zP}pGcvKz{e}1eK4&0 z{@KaOYUr(hmvwnCA5!+J7J7UiB^IN09?@ED)@yuWx-02{>=h$q8G3bp`}qFZIQ_5F z-1{q=Gy(eSL1mG~|7C*P-oOK6aieSa&TnIJgyVaaK0E=C+JSbSLhp=WCNO*&IW(;!>V58?MT{-Pius3b3fiNLFBv z6&&1q z&m|@#o_>jpy}`ZO((SKQM*n_a8J@bIMgqxvGRCu0Qkl*B)H*2zyPt77S3VvqKgnVc z9((%^h=|-A*MDhM1z^Q40EuWcM^Suxpr1t}kdmwRa5k40w^U6{Z5DJQZqd8%KMQw4 z!Yo(-Dk8#hrK?^?$ZePA0D*>7b~+h%PciirL!@BVi&JZ{2LRIXI01?qcW~Osal_ie zkd5M7K$@f#*YnU{ZKuZ{8wN!4%Kb0IIh7IKlVq)S-3e_vIe;8&>CuG(=5j@w6Hqc0 z+XVPlPKl0t{K>irGY}8t|#bjSXlOpVRn-&WX;Bhn%X#q#+J6pNm{66JxRcC2J(nygFtBSdi;% zBvFy%5+1XU2`LqI-Gf;nxd+GJ76VD5GLPfWh4xrnPv@ei^7(N4S@w#zL$;^*Cb;E+ zeZ^t)vSDL8x4W@_guS$<_?vSxt+*;i*(REK?6u4>WdSBpTcxa(% zz=fxUQuW^2)ium*%?#Rb4IYx6MUeCSlyn0%_aFmVkW2F8d*{zz+x-H#Pl{?FQg1dWT zSTA=`lkkvwxEsux?*t)y=lE@9K+PdV$7VRT6XhYj_$-Y098@?4_1M(=nxEr+g^O|0 zLF^xi*?jz1l?qQTy?loE^NSY)?RCpUFFzdBFgmeda%!&k+d)A!7*9xe)Nz@yF#HyL zcs_oM7h)YF-q}7Gd%Qa)9Aia3emEdp;(Ko6fnyM)U?s$6e;Vu8ksz|6XSd0gz-~Q1WFScjU#(OCoI(ydx1$Zt zbCUSop7Zc=|D6FX9yjNo6JgH}x&v$IiO4xWF6U!CjwTypin85z+Dsantza!nKL&JQ z<@Q?R!CM^|vt4-{v-+#_Zi+N^4U@d46N!;`U*A6hEQKim(utSwwaCY)hu`w(s`DPk z1UDwQ%xTI##gq@)3 z)iR0{VL$geb^Z^64(x{ThZ zQ36ke$eoGxsf#eG1NS~-y_vSHGgUJXGy%N8BX675zR-zYKkFUwAcQ1&_p7wYvwM51 zG`D?ZXDsJBy8@SG6`NfevMAd6u=i+O@tC_&kK2%vPeUkb>0x?S)}AH66xau`${Ak& zY5*yn%FMMvM_G8w4ljh%Z@+H_5YJ@#3;}a}sI?TkX8RMNYNm=5-RIW8S1^NnGrM%Y z=EjZ!g0*42TUfA6Yb4?(ZCBu!%%F3pD*BSy@bmDO3N~$8r~46;fyJCYbLDD5{EEU# z^rvA3*QQHx^wNv8B5~VG>yxxAr>nW({kE~$frQ9XSrA4tQIUz}dEUD{%oJVUn=>(_ zcIxEMh_#>*L+>T~6PpGpX_4*f6V9u7*!Zhk}8Zu z_lIR9wfg*A*=VaX7uSt@I?39N0o7F3$~q;V&|q^I?zC>|#G)i(U}#@0OgufEjJ;LLSOX@90OkJvS4@parGEFv;WB_IXGFe4c& zEl?B}Jdc=}Tf>Q|q(7bsD}bQL=hNz`ppE|nm$@OC0Pl>iq^S!%^6=HMsOh31qrFq~ zj!8R@vYRt zlvb13M?SDC@rObdepOhT7?nV-uN-AeSobG$9Q7TS!3LygMAh$NV<`7X-}!vs>aB!) za7ZX0rG2CdX|wR+bbDd=fpR0_uuAW5dl*6532QG$6l^Z@7mps406oHv{(MuJyozVg zqs&W>7QGPeHs}^{7%Mp^e{cS?m7gBOQ{B0`+prE!Q+KT*4>GD+2(`svAP|?H zE3pgGMfj1oeZiIv3WY|urvXvSYnABizr*0avFJzXPl^vazet*4<{;mOtg*Ad(5EKSGM(J3jr6b zEU_LQrX8fGrS#?^Rs>q5BclNu9`3MKQBjfbm>VirsYcpNP3uj|cs?OX^=3)&m&{dU zOIviywD-W0>GTaNdIfDADl6APs#*<21qiIqH}vO)>wqR57>=>tEC^oquNj!8JV;mk z+THoV{P1RjS}V*EEd#Xx1MmdLv8ckgVye!0mwcB+>^{g%XN4_SFLh|tm1UO<6dYx= zy$}8H?$N$L(pto)u(S`!pTexkGqNhZm)RgUxwhiXA3H5JDbu^2jy1U%2o(b95kMgqdHw_;p4sHLiedg zm^Dq>I1plLYZHb+LPyKG+agVQ^>)tAmf)o2QFG-4>vs@_$*Jilu9f#|Fjj`|A30dGGI9tgWO87ykA&wM)r|c znS87A&C`2$>3)=kPHjk7HoYH6`EhIBDIPa*a~=mtelz|SrSJ` z&}F~$I}hO~Lp%+r_1})lxZZFsZ0SjH?k^k-k)I3VFpi5#3YC-0t#_@Q<(3)taQC9W z(Sz`)@w?rdQLw!xbkx@(IjF~oF%o0tY#qVjTfJ%o{Yh3M4Uyg)YO~FMX~%nVGI@Vsebj;*N#y z{F9_YzV&%E$Wyz7mxn56E1aCmzC4M(;GhdYK^PGhtUsG2q<*%LGFUQrK>Db~jp>1D zb2lP zF&YpijOcjP22-c(h;9-i8FKcc#$GGbmq%RcSY%*dN>KxyMkSbsFw=UI$ZqJd$K7q) z2IAMZ4Jd8aYC{>nhX)moZ#Y~xJc459*C({QEWdyk?hrk9Jc-Z7)6L;-fmxy^7GrSAZ- zLr~+ggls-)q4g7x1=6vkhEPaO_zCKBXG;@hft3S4?)`FQ^^U2jfE;2aKLlgqE0NCM`BUFe~=@b6BJ^azWP z1|o6F#0&(6OEuw`?VvN9XRO^+p>Q9P+mbP0q;B%kX?r@!N7Ez&yimi9 z4u^z*{n>a+odoLyePl{x1n1AMTNGiWOKp*%*Jv3|ci~H+cwMgvbiIG5;u+mLZt$zU zRqF*B>ivlnp%^{s$+a;W(Ff{Fk+*9|*OBib3b%IBQa;ba_z(n|Tp<%I0Z~B`ECY5k!0ZGPLB+6Oh%t8tsY`UYt_j zGo)2wbOU+S_v`xx)d4j<8x{QXB8$ceWcQIMDemj>l8;dn6GOwm;3{@T;eAOh z;3LFCAF|2iT|)0<^4psoz=kO>6z~h!Ctg)naca1Aq)wy2WqG&N zo3=U=Us<}lv#C1i`3rC}%~0#7 z6b*;Ztxr{?BPaZvX#wW2P!)gF&lWz*-A@7nj@HoE+DVV6r=bGg=N}EWA*-`Jo>~o9elT?xFia^!ifI5Mdo5@-IM^Y$)BFny9h|lxUYirYW@{%(-@+bL` zXL%|V@O?JD_dI9HW>&K?bnleJdE_}H(jbMQCVEUtE|SkpzojY$w<^9_fp>eH^wa6> zTGyf$Y@eb}>m>DN)3%Sb-jP3V+uBqD#}ymdHY}a@pkF;uhwZXiUMz9eHL*n2R9Ra) zi9$n=o0%x84d&*m^gl~bG#VimMU_6)Md8pKWE#y!W~c z|HSsrH5o#Uuw$o1P#C9FldpAZE~PEU#^4J*aK#BUJN@-#aghslihwe*gnaWcbeneD8(y)qA(Q7S+8L|lpPG2qHOm@QXt5KhynrLld$LDqF{m>G}) zXY1F95XSn-&}B9|QsHNE0VU1o6NYkky^|{S+LwXCl-g!@prQyoDfvA5;dHlx#Z{YL zD46Oj*wR6tsuHM+FxVtKrPQkIsv6-nl9Sx# zfNzUwK6JdTob`6w+z=K!w};6TDW`E7TL(*xlx;uwY3|kv4iz}p6xmabPKm&cmbp=7 zaA?V7k-s$ihbEL+B<*2|{X+}x9 zZaqpWS&t51^p+b(cOjee8`3WC=j(vTdqpx7q*HP__mqmal+ahyygee#^0b+CO!
Z+ma_+}NiMW$z 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%0A$38EDk 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%0A#hDPJglT`{NW|gT02L+LJ<_v3a!7jEc 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 AsQ>@~ literal 0 HcmV?d00001 diff --git a/docs/images/ds_create_token.png b/docs/images/ds_create_token.png new file mode 100644 index 0000000000000000000000000000000000000000..539c0bdf1a94fae030c18ee4c76a7a2b41c33c8e GIT binary patch literal 268746 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ĥ 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 L^>L}#!jAt3Hq)@r literal 0 HcmV?d00001 diff --git a/docs/images/ds_create_user.png b/docs/images/ds_create_user.png new file mode 100644 index 0000000000000000000000000000000000000000..5117f398b4902522cffe9e7f7e4cfb7bbc33e956 GIT binary patch literal 294395 zcmeFZWmH^Ew=IkXNpK17nvmeZ-3jglcX#W?EqJitZoz^kIE{PI;M%xLHxg)^+j-w} z&bZ^;@4G+l^Y<`nk6rAst9I3@RjcNjwW_1lROGN;lD$MgK)_Ovm)1Z)c;$_NfU1v< z3V-sRAD0dR0a4seN=i*ZN{U9!-No9@(Fy^9CCS3n6kq-w%dnZ5sp;@I>zkME0F9`q zFB+yn6Wt@-gEZaUL){q}hK3s?xEsh?y$HqXJ|PqG3X0)JR2GG57R0HE z+ZU-anTMo|FXOv$KWd+iDbCNRb8vBRS(_q+^GRRXaJuqy#pFsx5#4Ze=%sOTa4^#K zZyA!1G_i7XoDtLY^8`O_salxwlNcrHrJw^4w)xVwcr57Av&*7FLav6MpYyFkjMoOb z&zcXOpVvK}pPxbSKHM@P_k~Czcr6*UzNOekgNxMBN>9OBSsCFiJdcimf=Gt&0-i&J zU!sWQ|CN_TWI;gw*K;HUgh)FCl>c}~1%Chgmk7W9mihM`sQ~Fe-+1L+fc&3%RQy$}^N7|sw7#1RyvCA5HuC%I@jMsr>e*cYB7r&*A-YzIs> zU0M!_oI~c1c}~CE)@MR7p$R|sCr0*347abuLcFD<794ZI2vc~H!Q%MJ`0|ouURvr< ztG#NM^-uc{zrCE5%0Jb&U;(FmqbFFN$Mw?vibHz9(?E=`6g?t7%|A;lBq0}}C1alz zf>GrES$Y>z3LOi|X8DKJz>B|>52gaWLeE!7M!-n?M+seRaeN)|A69}vn1K)ue({8q zA^cBk`rDj;)S@n!qv4Hn0ne*}P7jF1@L zoEUdKx7-4Uriw~BB`%sjNv|b?8aa#%c;77hb6GHmBmI*!AW_gx^7F?>VHaixIhFZr zRqxleVnd;P7{IP$aF!S9`LMDxuCoAFux_|Vs*U+*4zO0?$bsv9^Lv-rh)g&Ni*kENd_z(Swuk?u?b8d-c z5RQaZT(5PJPK(tX97hwuUP#mUh;mmGk=tlr*gsM;xPeg1Q$#uIKWzpL>H6lifh~ST zwc&Mo$+VI#wYK`%tJm&*2k#c>`9SzPz5`)8XH1qky`|OOo>g=r7 zTh-aJ|5ST^;SXol`K1D9CL+CXLjaL`aUH??^iHM!e`=X15VEiiq`#EM?^n|_a*~>z zon=7(^)I`UNP)Np`nuh(_FtpF#8_vq+wtj5KC#Y1wge?T_K-Ce%jJB1Hkrl$>1hx1 zg;2Z%B@CrlxiStcaEiD5y9ur%c)zj1mo{VkyCZ{0N&mZj7q3BIy+6KJI$dsTjS*#9 zyR=c_BlI4k*+j)i6wg8dLO&8vsQ%Z7}#~gMLDR9R7Qt*g_?d6jOnMWnd9^JiBON}KS5Lo19|~Oy3hi@ zfaEiZe8CPzL3KC`^!1tSQ!pJ30_XLsEw>ey^ZwPRy0&UMm#am}onPd}?VDFZ@(eZS zuKyO`|Bjc`_>lw%yeGfgSwKGhTwz^$)rf|5ZPgba6A4lEruu;EO`Po{=-RuXIn&wu z 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

1d_|o7aG1yG(c{ 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

2(fEQ}RXWx*tV#YZY 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º_dA_j_^n<9 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 LJ)CmT^2+}Lz7mHF literal 0 HcmV?d00001 diff --git a/seatunnel-server/seatunnel-app/pom.xml b/seatunnel-server/seatunnel-app/pom.xml index aef911857..aa41a1588 100644 --- a/seatunnel-server/seatunnel-app/pom.xml +++ b/seatunnel-server/seatunnel-app/pom.xml @@ -246,26 +246,73 @@ - - - maven-assembly-plugin - - - seatunnel-web - - single - - package - - seatunnel-web - - src/main/assembly/seatunnel-web.xml - - false - - - - + + + release + + + release + true + + + + apache-seatunnel-web-incubating-${project.version} + + + maven-assembly-plugin + + + seatunnel-web + + single + + package + + + src/main/assembly/seatunnel-web.xml + + false + + + + + + + + + ci + + true + + release + true + + + + apache-seatunnel-web-incubating-${project.version} + + + maven-assembly-plugin + + + seatunnel-web + + single + + package + + + src/main/assembly/seatunnel-web-ci.xml + + false + + + + + + + + \ No newline at end of file diff --git a/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml b/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml new file mode 100644 index 000000000..91fe5e3c3 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml @@ -0,0 +1,78 @@ + + + + seatunnel-web + + dir + zip + tar.gz + + false + + + ${basedir}/src/main/resources + + *.yml + *.yaml + *.xml + + conf + + + ${basedir}/src/main/resources/script + + *.* + + script + + + ${basedir}/src/main/bin + + *.sh + + bin + + + ${basedir}/../../seatunnel-ui/dist + + **/*.* + + ui + + + + + libs + + + + false + true + false + + mysql:mysql-connector-java:jar + org.postgresql:postgresql:jar + + ${artifact.file.name} + libs + provided + + + diff --git a/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml b/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml index 0d1f4935a..5dce8b831 100644 --- a/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml +++ b/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml @@ -24,7 +24,6 @@ zip false - seatunnel-web ${basedir}/src/main/resources @@ -35,6 +34,13 @@ conf + + ${basedir}/src/main/resources/script + + *.* + + script + ${basedir}/src/main/bin @@ -45,9 +51,9 @@ ${basedir}/../../seatunnel-ui/dist - * + **/*.* - dist + ui diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java index 9f1d4ab0f..84117d8ef 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java @@ -23,15 +23,26 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.CookieLocaleResolver; import javax.annotation.Resource; import java.util.List; +import java.util.Locale; @Configuration public class SeatunnelWebAdapter implements WebMvcConfigurer { + + public static final String LOCALE_LANGUAGE_COOKIE = "language"; + public static final String LOGIN_INTERCEPTOR_PATH_PATTERN = "/**/*"; + public static final String LOGIN_PATH_PATTERN = "/seatunnel/api/v1/user/login**"; + public static final String REGISTER_PATH_PATTERN = "/users/register"; + @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); @@ -40,23 +51,47 @@ public AuthenticationInterceptor authenticationInterceptor() { @Resource private UserIdMethodArgumentResolver currentUserMethodArgumentResolver; + /** + * Cookie + * + * @return local resolver + */ + @Bean(name = "localeResolver") + public LocaleResolver localeResolver() { + CookieLocaleResolver localeResolver = new CookieLocaleResolver(); + localeResolver.setCookieName(LOCALE_LANGUAGE_COOKIE); + // set default locale + localeResolver.setDefaultLocale(Locale.US); + // set language tag compliant + localeResolver.setLanguageTagCompliant(false); + return localeResolver; + } + @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(authenticationInterceptor()).order(1).addPathPatterns("/**") - // exclude swagger api path - .excludePathPatterns( - "/swagger-resources/**", - "/webjars/**", - "/v2/**", - "/swagger-ui.html**" - ) - // exclude login - .excludePathPatterns("/api/v1/user/login**") - ; + registry.addInterceptor(authenticationInterceptor()) + .order(1) + .addPathPatterns(LOGIN_INTERCEPTOR_PATH_PATTERN) + .excludePathPatterns(LOGIN_PATH_PATTERN, REGISTER_PATH_PATTERN, + "/swagger-resources/**", "/webjars/**", "/v2/**", "*.html", "/ui/**", "/error", "/swagger-ui.html**"); } @Override public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(currentUserMethodArgumentResolver); } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + registry.addResourceHandler("/ui/**").addResourceLocations("file:ui/"); + } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/").setViewName("redirect:/ui/"); + registry.addViewController("/ui/").setViewName("forward:/ui/index.html"); + } + } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java index fa83416fd..b2d99503f 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java @@ -31,7 +31,7 @@ import javax.annotation.Resource; import javax.validation.constraints.NotNull; -@RequestMapping("/api/v1/auth") +@RequestMapping("/seatunnel/api/v1/auth") @RestController public class AuthController { diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java index ff094d436..e1885e295 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java @@ -50,7 +50,7 @@ import java.util.List; -@RequestMapping("/api/v1/script") +@RequestMapping("/seatunnel/api/v1/script") @RestController public class ScriptController { @Resource diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java index 43f141d71..b66de54dc 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java @@ -44,7 +44,7 @@ import javax.annotation.Resource; import javax.validation.constraints.NotNull; -@RequestMapping("/api/v1/task") +@RequestMapping("/seatunnel/api/v1/task") @RestController public class TaskController { diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java index 08cd90723..6a818496a 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java @@ -43,7 +43,7 @@ import javax.annotation.Resource; import javax.validation.constraints.NotNull; -@RequestMapping("/api/v1/user") +@RequestMapping("/seatunnel/api/v1/user") @RestController public class UserController { diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/interceptor/AuthenticationInterceptor.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/interceptor/AuthenticationInterceptor.java index 3b4326256..05a54fa3d 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/interceptor/AuthenticationInterceptor.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/interceptor/AuthenticationInterceptor.java @@ -20,16 +20,15 @@ import static org.apache.seatunnel.server.common.Constants.OPTIONS; import static org.apache.seatunnel.server.common.Constants.TOKEN; import static org.apache.seatunnel.server.common.Constants.USER_ID; -import static org.apache.seatunnel.server.common.SeatunnelErrorEnum.TOKEN_ILLEGAL; import static io.jsonwebtoken.Claims.EXPIRATION; import org.apache.seatunnel.app.dal.dao.IUserDao; import org.apache.seatunnel.app.dal.entity.UserLoginLog; import org.apache.seatunnel.app.security.JwtUtils; -import org.apache.seatunnel.server.common.SeatunnelException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.http.HttpStatus; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @@ -60,24 +59,33 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons response.setHeader("Access-Control-Max-Age", "3600"); return true; } + long currentTimestamp = System.currentTimeMillis(); final String token = request.getHeader(TOKEN); if (StringUtils.isBlank(token)) { - throw new SeatunnelException(TOKEN_ILLEGAL); + log.info("user does not exist"); + response.setStatus(HttpStatus.UNAUTHORIZED_401); + return false; } final Map map = jwtUtils.parseToken(token); final Integer userId = (Integer) map.get(USER_ID); if (Objects.isNull(userId)) { - throw new SeatunnelException(TOKEN_ILLEGAL); + log.info("userId does not exist"); + response.setStatus(HttpStatus.UNAUTHORIZED_401); + return false; } final UserLoginLog userLoginLog = userDaoImpl.getLastLoginLog(userId); if (Objects.isNull(userLoginLog) || !userLoginLog.getTokenStatus()) { - throw new SeatunnelException(TOKEN_ILLEGAL); + log.info("userLoginLog does not exist"); + response.setStatus(HttpStatus.UNAUTHORIZED_401); + return false; } final Integer expireDate = (Integer) map.get(EXPIRATION); if (Objects.isNull(expireDate) || currentTimestamp - (long) expireDate * 1000 > 0) { - throw new SeatunnelException(TOKEN_ILLEGAL); + log.info("user token has expired"); + response.setStatus(HttpStatus.UNAUTHORIZED_401); + return false; } map.forEach(request::setAttribute); diff --git a/seatunnel-server/seatunnel-app/src/main/resources/application.yml b/seatunnel-server/seatunnel-app/src/main/resources/application.yml index 77e35480e..7d9f51afc 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/application.yml +++ b/seatunnel-server/seatunnel-app/src/main/resources/application.yml @@ -25,7 +25,7 @@ spring: date-format: yyyy-MM-dd HH:mm:ss datasource: driver-class-name: com.mysql.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/seatunnel?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true + url: jdbc:mysql://localhost:3306/seatunnel?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&allowPublicKeyRetrieval=true username: root password: 123456 mvc: @@ -35,11 +35,11 @@ ds: script: dir: /dj project: - default: test_dj + default: seatunnel_test tenant: - default: default + default: gaojun api: - token: 12345678 + token: 296fecc6e5f78e6a5aa39106707476fe prefix: http://127.0.0.1:12345/dolphinscheduler jwt: diff --git a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_env.sh b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_env.sh index 3ec563f39..5fdd3459f 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_env.sh +++ b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_env.sh @@ -15,7 +15,7 @@ # limitations under the License. # -export HOSTNAME="" -export PORT="" -export USERNAME="" -export PASSWORD="" \ No newline at end of file +export HOSTNAME="127.0.0.1" +export PORT="3306" +export USERNAME="root" +export PASSWORD="123456" \ No newline at end of file diff --git a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql index 8ee41d344..696abf118 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql +++ b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql @@ -171,3 +171,5 @@ CREATE TABLE `user_login_log` ( -- ---------------------------- -- Records of user_login_log -- ---------------------------- + +INSERT INTO `seatunnel`.`user`(`username`,`password`,`status`,`type`) values ('admin', '7f97da8846fed829bb8d1fd9f8030f3b', 0, 0); \ No newline at end of file diff --git a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql index e933890b0..a7a84e9d2 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql +++ b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql @@ -171,3 +171,5 @@ CREATE TABLE `seatunnel`.`user_login_log` ( -- ---------------------------- -- Records of user_login_log -- ---------------------------- + +INSERT INTO `seatunnel`.`user`(`username`,`password`,`status`,`type`) values ('admin', '7f97da8846fed829bb8d1fd9f8030f3b', 0, 0); diff --git a/seatunnel-ui/src/router/index.ts b/seatunnel-ui/src/router/index.ts index 188a9f449..fba0d6b97 100644 --- a/seatunnel-ui/src/router/index.ts +++ b/seatunnel-ui/src/router/index.ts @@ -21,7 +21,9 @@ import NProgress from 'nprogress' import 'nprogress/nprogress.css' const router = createRouter({ - history: createWebHistory('/'), + history: createWebHistory( + import.meta.env.MODE === 'production' ? '/ui/' : '/' + ), routes }) diff --git a/seatunnel-ui/src/router/routes.ts b/seatunnel-ui/src/router/routes.ts index 8397eecff..64eac4680 100644 --- a/seatunnel-ui/src/router/routes.ts +++ b/seatunnel-ui/src/router/routes.ts @@ -26,7 +26,11 @@ import type { Component } from 'vue' const modules = import.meta.glob('/src/views/**/**.tsx') const components: { [key: string]: Component } = utils.mapping(modules) -const basePage: RouteRecordRaw[] = [dataPipes, jobs, tasks, userManage] +const basePage: RouteRecordRaw[] = [{ + path: '/', + redirect: { name: 'data-pipes' } + }, + dataPipes, jobs, tasks, userManage] const loginPage: RouteRecordRaw[] = [ { diff --git a/seatunnel-ui/src/service/service.ts b/seatunnel-ui/src/service/service.ts index 8897f28d0..8f3de6b9e 100644 --- a/seatunnel-ui/src/service/service.ts +++ b/seatunnel-ui/src/service/service.ts @@ -42,7 +42,7 @@ const baseRequestConfig: AxiosRequestConfig = { timeout: settingStore.getRequestTimeValue ? settingStore.getRequestTimeValue : 6000, - baseURL: '/api/v1' + baseURL: '/seatunnel/api/v1' } const service = axios.create(baseRequestConfig) diff --git a/seatunnel-ui/vite.config.ts b/seatunnel-ui/vite.config.ts index 237ef0aa8..d7498d01d 100644 --- a/seatunnel-ui/vite.config.ts +++ b/seatunnel-ui/vite.config.ts @@ -22,6 +22,7 @@ import viteCompression from 'vite-plugin-compression' import path from 'path' export default defineConfig({ + base: process.env.NODE_ENV === 'production' ? '/ui/' : '/', plugins: [ vue(), vueJsx(), @@ -42,7 +43,7 @@ export default defineConfig({ }, server: { proxy: { - '/api/v1': { + '/seatunnel/api/v1': { target: loadEnv('development', './').VITE_APP_DEV_WEB_URL, changeOrigin: true } From fa2e3333d4fe338204a58d2d8b565151e8f126dc Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 11 May 2023 04:22:15 +0800 Subject: [PATCH 06/28] [Feature][DynamicForm] Add seatunnel dynamicform module (#49) --- .github/workflows/backend.yml | 2 +- pom.xml | 148 +++++++- seatunnel-server/pom.xml | 1 + seatunnel-server/seatunnel-app/pom.xml | 4 + .../src/main/resources/application.yml | 2 +- .../app/controller/UserControllerTest.java | 9 +- .../seatunnel-dynamicform/pom.xml | 57 +++ .../app/dynamicforms/AbstractFormOption.java | 118 +++++++ .../AbstractFormSelectOption.java | 49 +++ .../seatunnel/app/dynamicforms/Constants.java | 25 ++ .../app/dynamicforms/DynamicSelectOption.java | 33 ++ .../app/dynamicforms/FormInputOption.java | 56 +++ .../app/dynamicforms/FormLocale.java | 51 +++ .../app/dynamicforms/FormOptionBuilder.java | 165 +++++++++ .../app/dynamicforms/FormStructure.java | 65 ++++ .../dynamicforms/FormStructureBuilder.java | 74 ++++ .../dynamicforms/FormStructureValidate.java | 273 +++++++++++++++ .../app/dynamicforms/StaticSelectOption.java | 38 ++ .../FormStructureValidateException.java | 35 ++ .../validate/AbstractValidate.java | 65 ++++ .../validate/MutuallyExclusiveValidate.java | 42 +++ .../validate/NonEmptyValidate.java | 30 ++ .../validate/UnionNonEmptyValidate.java | 45 +++ .../validate/ValidateBuilder.java | 78 +++++ .../FormStructureBuilderTest.java | 329 ++++++++++++++++++ .../seatunnel/app/dynamicforms/TestUtils.java | 34 ++ .../src/test/resources/test_form.json | 1 + .../seatunnel-server-common/pom.xml | 7 +- seatunnel-ui/src/router/routes.ts | 2 +- tools/dependencies/known-dependencies.txt | 7 +- 30 files changed, 1832 insertions(+), 13 deletions(-) create mode 100644 seatunnel-server/seatunnel-dynamicform/pom.xml create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormSelectOption.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/Constants.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/DynamicSelectOption.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormInputOption.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormLocale.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormOptionBuilder.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructure.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilder.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/StaticSelectOption.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/exception/FormStructureValidateException.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/AbstractValidate.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/MutuallyExclusiveValidate.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/NonEmptyValidate.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/UnionNonEmptyValidate.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/ValidateBuilder.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilderTest.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/TestUtils.java create mode 100644 seatunnel-server/seatunnel-dynamicform/src/test/resources/test_form.json diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index efc139b19..eea3507e5 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -125,7 +125,7 @@ jobs: cache: 'maven' - name: Install run: >- - ./mvnw -B -q install -DskipTests + ./mvnw -B -q install -DskipTests -P release -D"maven.test.skip"=true -D"maven.javadoc.skip"=true -D"checkstyle.skip"=true diff --git a/pom.xml b/pom.xml index e5195ce02..070562e0f 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,6 @@ false - 2.1.3 1.2 1.7.25 2.12.7.1 @@ -113,14 +112,157 @@ 3.10.0 2.17.1 4.2.0 + 2.3.1 + org.apache.seatunnel seatunnel-common - ${seatunnel-common.version} + ${seatunnel-framework.version} + + + jcl-over-slf4j + org.slf4j + + + log4j-1.2-api + org.apache.logging.log4j + + + log4j-api + org.apache.logging.log4j + + + log4j-core + org.apache.logging.log4j + + + log4j-slf4j-impl + org.apache.logging.log4j + + + + + + org.apache.seatunnel + seatunnel-jackson + ${seatunnel-framework.version} + optional + + + com.fasterxml.jackson.dataformat + jackson-dataformat-properties + + + slf4j-log4j12 + org.slf4j + + + log4j-1.2-api + org.apache.logging.log4j + + + jcl-over-slf4j + org.slf4j + + + log4j-api + org.apache.logging.log4j + + + log4j-core + org.apache.logging.log4j + + + log4j-slf4j-impl + org.apache.logging.log4j + + + + + + org.apache.seatunnel + seatunnel-api + ${seatunnel-framework.version} + + + jcl-over-slf4j + org.slf4j + + + log4j-1.2-api + org.apache.logging.log4j + + + log4j-api + org.apache.logging.log4j + + + log4j-core + org.apache.logging.log4j + + + log4j-slf4j-impl + org.apache.logging.log4j + + + + + + org.apache.seatunnel + seatunnel-engine-client + ${seatunnel-framework.version} + + + + com.google.auto.service + auto-service + ${auto-service.version} + provided + + + + org.apache.seatunnel + seatunnel-plugin-discovery + ${seatunnel-framework.version} + + + + org.apache.seatunnel + seatunnel-transforms-v2 + ${seatunnel-framework.version} + + + + + org.apache.seatunnel + connector-common + ${seatunnel-framework.version} + test + + + + org.apache.seatunnel + connector-console + ${seatunnel-framework.version} + test + + + + org.apache.seatunnel + connector-fake + ${seatunnel-framework.version} + test + + + + org.apache.seatunnel + connector-jdbc + ${seatunnel-framework.version} + test @@ -153,7 +295,7 @@ jackson-databind ${jackson.version} - + com.google.guava guava diff --git a/seatunnel-server/pom.xml b/seatunnel-server/pom.xml index 79ef3a310..d6bd26dfc 100644 --- a/seatunnel-server/pom.xml +++ b/seatunnel-server/pom.xml @@ -27,6 +27,7 @@ pom seatunnel-app + seatunnel-dynamicform seatunnel-spi seatunnel-scheduler seatunnel-server-common diff --git a/seatunnel-server/seatunnel-app/pom.xml b/seatunnel-server/seatunnel-app/pom.xml index aa41a1588..0e94b6549 100644 --- a/seatunnel-server/seatunnel-app/pom.xml +++ b/seatunnel-server/seatunnel-app/pom.xml @@ -93,6 +93,10 @@ spring-context org.springframework + + com.fasterxml.jackson.core + jackson-annotations + diff --git a/seatunnel-server/seatunnel-app/src/main/resources/application.yml b/seatunnel-server/seatunnel-app/src/main/resources/application.yml index 7d9f51afc..a873c088b 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/application.yml +++ b/seatunnel-server/seatunnel-app/src/main/resources/application.yml @@ -40,7 +40,7 @@ ds: default: gaojun api: token: 296fecc6e5f78e6a5aa39106707476fe - prefix: http://127.0.0.1:12345/dolphinscheduler + prefix: http://localhost:12345/dolphinscheduler jwt: expireTime: 86400 diff --git a/seatunnel-server/seatunnel-app/src/test/java/org/apache/seatunnel/app/controller/UserControllerTest.java b/seatunnel-server/seatunnel-app/src/test/java/org/apache/seatunnel/app/controller/UserControllerTest.java index 4b1b9116d..20b706c90 100644 --- a/seatunnel-server/seatunnel-app/src/test/java/org/apache/seatunnel/app/controller/UserControllerTest.java +++ b/seatunnel-server/seatunnel-app/src/test/java/org/apache/seatunnel/app/controller/UserControllerTest.java @@ -28,7 +28,8 @@ import org.apache.seatunnel.app.domain.response.user.AddUserRes; import org.apache.seatunnel.common.utils.JsonUtils; -import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.seatunnel.shade.com.fasterxml.jackson.core.type.TypeReference; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -38,6 +39,8 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import java.lang.reflect.Type; + @Disabled("todo:this test is not working, waiting fix") public class UserControllerTest extends WebMvcApplicationTest { @@ -59,6 +62,10 @@ public void testAdd() throws Exception { .andReturn(); String result = mvcResult.getResponse().getContentAsString(); Result data = JsonUtils.parseObject(result, new TypeReference>() { + @Override + public Type getType() { + return super.getType(); + } }); Assertions.assertTrue(data.isSuccess()); Assertions.assertNotNull(data.getData()); diff --git a/seatunnel-server/seatunnel-dynamicform/pom.xml b/seatunnel-server/seatunnel-dynamicform/pom.xml new file mode 100644 index 000000000..36376f75d --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/pom.xml @@ -0,0 +1,57 @@ + + + + 4.0.0 + + seatunnel-server + org.apache.seatunnel + 1.0.0-SNAPSHOT + + + seatunnel-dynamicform + + + + com.google.guava + guava + + + + org.apache.commons + commons-lang3 + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.apache.seatunnel + seatunnel-common + + + org.apache.seatunnel + seatunnel-jackson + optional + + + diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java new file mode 100644 index 000000000..4630bbb37 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.app.dynamicforms.validate.AbstractValidate; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonInclude; +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.Getter; +import lombok.NonNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Data +public abstract class AbstractFormOption { + + // support i18n + private final String label; + private final String field; + private Object defaultValue; + + // support i18n + private String description = ""; + private boolean clearable; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map show; + + // support i18n + private String placeholder = ""; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private V validate; + + public AbstractFormOption(@NonNull String label, @NonNull String field) { + this.label = label; + this.field = field; + } + + public enum FormType { + @JsonProperty("input") + INPUT("input"), + + @JsonProperty("select") + SELECT("select"); + + @Getter + private String formType; + + FormType(String formType) { + this.formType = formType; + } + } + + public T withShow(@NonNull String field, @NonNull List values) { + if (this.show == null) { + this.show = new HashMap<>(); + } + + this.show.put(Constants.SHOW_FIELD, field); + this.show.put(Constants.SHOW_VALUE, values); + return (T) this; + } + + public T withValidate(@NonNull V validate) { + this.validate = validate; + return (T) this; + } + + public T withDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + return (T) this; + } + + public T withDescription(@NonNull String description) { + this.description = description; + return (T) this; + } + + public T withI18nDescription(@NonNull String description) { + this.description = FormLocale.I18N_PREFIX + description; + return (T) this; + } + + public T withClearable() { + this.clearable = true; + return (T) this; + } + + public T withPlaceholder(@NonNull String placeholder) { + this.placeholder = placeholder; + return (T) this; + } + + public T withI18nPlaceholder(@NonNull String placeholder) { + this.placeholder = FormLocale.I18N_PREFIX + placeholder; + return (T) this; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormSelectOption.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormSelectOption.java new file mode 100644 index 000000000..52b0c534c --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormSelectOption.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; +import lombok.NonNull; + +public abstract class AbstractFormSelectOption extends AbstractFormOption { + + @JsonProperty("type") + @Getter + private final FormType formType = FormType.SELECT; + + public AbstractFormSelectOption(@NonNull String label, @NonNull String field) { + super(label, field); + } + + public static class SelectOption { + @JsonProperty + @Getter + private String label; + + @JsonProperty + @Getter + private Object value; + + public SelectOption(@NonNull String label, @NonNull Object value) { + this.label = label; + this.value = value; + } + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/Constants.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/Constants.java new file mode 100644 index 000000000..df4ac2dca --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/Constants.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +public final class Constants { + + public static final String SHOW_FIELD = "field"; + + public static final String SHOW_VALUE = "value"; +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/DynamicSelectOption.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/DynamicSelectOption.java new file mode 100644 index 000000000..d1365cd67 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/DynamicSelectOption.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; + +public class DynamicSelectOption extends AbstractFormSelectOption { + @Getter + @Setter + private String api; + + public DynamicSelectOption(@NonNull String api, @NonNull String label, @NonNull String field) { + super(label, field); + this.api = api; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormInputOption.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormInputOption.java new file mode 100644 index 000000000..f71d64ac9 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormInputOption.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; +import lombok.NonNull; + +public class FormInputOption extends AbstractFormOption { + @JsonProperty("type") + @Getter + private final FormType formType = FormType.INPUT; + + @Getter + private final InputType inputType; + + public FormInputOption( + @NonNull InputType inputType, @NonNull String label, @NonNull String field) { + super(label, field); + this.inputType = inputType; + } + + public enum InputType { + @JsonProperty("text") + TEXT("text"), + + @JsonProperty("password") + PASSWORD("password"), + + @JsonProperty("textarea") + TEXTAREA("textarea"); + + @Getter + private String inputType; + + InputType(String inputType) { + this.inputType = inputType; + } + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormLocale.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormLocale.java new file mode 100644 index 000000000..466b03c6c --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormLocale.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonIgnore; +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.NonNull; + +import java.util.LinkedHashMap; + +/** + * Multi language support + */ +@Data +public class FormLocale { + @JsonIgnore + public static final String I18N_PREFIX = "i18n."; + + @JsonProperty("zh_CN") + private LinkedHashMap zhCN = new LinkedHashMap<>(); + + @JsonProperty("en_US") + private LinkedHashMap enUS = new LinkedHashMap<>(); + + public FormLocale addZhCN(@NonNull String key, @NonNull String value) { + zhCN.put(key, value); + return this; + } + + public FormLocale addEnUS(@NonNull String key, @NonNull String value) { + enUS.put(key, value); + return this; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormOptionBuilder.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormOptionBuilder.java new file mode 100644 index 000000000..8c645abf8 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormOptionBuilder.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import lombok.NonNull; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import java.util.ArrayList; +import java.util.List; + +public class FormOptionBuilder { + + private String label; + + private String field; + + public static FormOptionBuilder builder() { + return new FormOptionBuilder(); + } + + public FormOptionBuilder withLabel(@NonNull String label) { + this.label = label; + return this; + } + + public FormOptionBuilder withI18nLabel(@NonNull String label) { + this.label = FormLocale.I18N_PREFIX + label; + return this; + } + + public FormOptionBuilder withField(@NonNull String field) { + this.field = field; + return this; + } + + public InputOptionBuilder inputOptionBuilder() { + return new InputOptionBuilder(label, field); + } + + public DynamicSelectOptionBuilder dynamicSelectOptionBuilder() { + return new DynamicSelectOptionBuilder(label, field); + } + + public StaticSelectOptionBuilder staticSelectOptionBuilder() { + return new StaticSelectOptionBuilder(label, field); + } + + public static class InputOptionBuilder { + private String label; + + private String field; + + public InputOptionBuilder(@NonNull String label, @NonNull String field) { + this.label = label; + this.field = field; + } + + public FormInputOption formTextInputOption() { + return new FormInputOption(FormInputOption.InputType.TEXT, label, field); + } + + public FormInputOption formPasswordInputOption() { + return new FormInputOption(FormInputOption.InputType.PASSWORD, label, field); + } + + public FormInputOption formTextareaInputOption() { + return new FormInputOption(FormInputOption.InputType.TEXTAREA, label, field); + } + } + + public static class DynamicSelectOptionBuilder { + private String label; + + private String field; + + private String selectApi; + + public DynamicSelectOptionBuilder(@NonNull String label, @NonNull String field) { + this.label = label; + this.field = field; + } + + public DynamicSelectOptionBuilder withSelectApi(@NonNull String selectApi) { + this.selectApi = selectApi; + return this; + } + + public DynamicSelectOption formDynamicSelectOption() { + return new DynamicSelectOption(selectApi, label, field); + } + } + + public static class StaticSelectOptionBuilder { + private String label; + + private String field; + + private List options = new ArrayList<>(); + + public StaticSelectOptionBuilder(@NonNull String label, @NonNull String field) { + this.label = label; + this.field = field; + } + + public StaticSelectOptionBuilder addSelectOptions( + @NonNull List selectOptions) { + for (ImmutablePair option : selectOptions) { + options.add( + new AbstractFormSelectOption.SelectOption( + option.left.toString(), option.right.toString())); + } + return this; + } + + public StaticSelectOptionBuilder addI18nSelectOptions( + @NonNull List selectOptions) { + for (ImmutablePair option : selectOptions) { + options.add( + new AbstractFormSelectOption.SelectOption( + FormLocale.I18N_PREFIX + option.left.toString(), + option.right.toString())); + } + return this; + } + + public StaticSelectOptionBuilder addSelectOptions(@NonNull ImmutablePair... selectOptions) { + for (ImmutablePair option : selectOptions) { + options.add( + new AbstractFormSelectOption.SelectOption( + option.left.toString(), option.right.toString())); + } + return this; + } + + public StaticSelectOptionBuilder addI18nSelectOptions( + @NonNull ImmutablePair... selectOptions) { + for (ImmutablePair option : selectOptions) { + options.add( + new AbstractFormSelectOption.SelectOption( + FormLocale.I18N_PREFIX + option.left.toString(), + option.right.toString())); + } + return this; + } + + public StaticSelectOption formStaticSelectOption() { + return new StaticSelectOption(options, label, field); + } + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructure.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructure.java new file mode 100644 index 000000000..6096b5969 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructure.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonIgnoreType; +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonInclude; + +import lombok.Data; +import lombok.NonNull; + +import java.util.List; +import java.util.Map; + +/** SeaTunnel Web UI will use this json data to automatically create page form elements */ +@Data +public class FormStructure { + private String name; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private FormLocale locales; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map> apis; + + private List forms; + + public FormStructure() {} + + public FormStructure( + @NonNull String name, + @NonNull List formOptionList, + FormLocale locale, + Map> apis) { + this.name = name; + this.forms = formOptionList; + this.locales = locale; + this.apis = apis; + } + + @JsonIgnoreType + public enum HttpMethod { + GET, + + POST + } + + public static FormStructureBuilder builder() { + return new FormStructureBuilder(); + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilder.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilder.java new file mode 100644 index 000000000..ba863680e --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilder.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.app.dynamicforms.exception.FormStructureValidateException; + +import lombok.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FormStructureBuilder { + private String name; + + private List forms = new ArrayList<>(); + + private FormLocale locales; + + private Map> apis; + + public FormStructureBuilder name(@NonNull String name) { + this.name = name; + return this; + } + + public FormStructureBuilder addFormOption(@NonNull AbstractFormOption... formOptions) { + for (AbstractFormOption formOption : formOptions) { + forms.add(formOption); + } + return this; + } + + public FormStructureBuilder withLocale(FormLocale locale) { + this.locales = locale; + return this; + } + + public FormStructureBuilder addApi( + @NonNull String apiName, + @NonNull String url, + @NonNull FormStructure.HttpMethod method) { + if (apis == null) { + apis = new HashMap<>(); + } + apis.putIfAbsent(apiName, new HashMap<>()); + apis.get(apiName).put("url", url); + apis.get(apiName).put("method", method.name().toLowerCase(java.util.Locale.ROOT)); + + return this; + } + + public FormStructure build() throws FormStructureValidateException { + FormStructure formStructure = new FormStructure(name, forms, locales, apis); + FormStructureValidate.validateFormStructure(formStructure); + return formStructure; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java new file mode 100644 index 000000000..025fe313b --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java @@ -0,0 +1,273 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.app.dynamicforms.exception.FormStructureValidateException; +import org.apache.seatunnel.app.dynamicforms.validate.AbstractValidate; +import org.apache.seatunnel.app.dynamicforms.validate.MutuallyExclusiveValidate; +import org.apache.seatunnel.app.dynamicforms.validate.UnionNonEmptyValidate; + +import lombok.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Check whether the form structure is correct + */ +public class FormStructureValidate { + + /** + * validate rules + */ + public static void validateFormStructure(@NonNull FormStructure formStructure) + throws FormStructureValidateException { + + List apiErrorList = validateApiOption(formStructure); + List localeErrorList = validateLocaleOption(formStructure); + List showErrorList = validateShow(formStructure); + List unionNonErrorList = validateUnionNonEmpty(formStructure); + List exclusiveErrorList = validateMutuallyExclusive(formStructure); + + apiErrorList.addAll(localeErrorList); + apiErrorList.addAll(showErrorList); + apiErrorList.addAll(unionNonErrorList); + apiErrorList.addAll(exclusiveErrorList); + + if (apiErrorList.size() > 0) { + throw new FormStructureValidateException(formStructure.getName(), apiErrorList); + } + } + + private static List validateApiOption(@NonNull FormStructure formStructure) { + List errorMessageList = new ArrayList(); + Map> apis = formStructure.getApis(); + formStructure + .getForms() + .forEach( + formOption -> { + if (formOption instanceof DynamicSelectOption) { + String api = ((DynamicSelectOption) formOption).getApi(); + if (apis == null || !apis.keySet().contains(api)) { + errorMessageList.add( + String.format( + "DynamicSelectOption[%s] used api[%s] can not found in FormStructure.apis", + ((DynamicSelectOption) formOption).getLabel(), + api)); + } + } + }); + return errorMessageList; + } + + private static List validateLocaleOption(@NonNull FormStructure formStructure) { + List errorMessageList = new ArrayList(); + FormLocale locales = formStructure.getLocales(); + formStructure + .getForms() + .forEach( + formOption -> { + if (formOption.getLabel().startsWith(FormLocale.I18N_PREFIX)) { + String labelName = + formOption.getLabel().replace(FormLocale.I18N_PREFIX, ""); + validateOneI18nOption( + locales, + formOption.getLabel(), + "label", + labelName, + errorMessageList); + } + + if (formOption.getDescription().startsWith(FormLocale.I18N_PREFIX)) { + String description = + formOption + .getDescription() + .replace(FormLocale.I18N_PREFIX, ""); + validateOneI18nOption( + locales, + formOption.getLabel(), + "description", + description, + errorMessageList); + } + + if (formOption.getPlaceholder().startsWith(FormLocale.I18N_PREFIX)) { + String placeholder = + formOption + .getPlaceholder() + .replace(FormLocale.I18N_PREFIX, ""); + validateOneI18nOption( + locales, + formOption.getLabel(), + "placeholder", + placeholder, + errorMessageList); + } + + AbstractValidate validate = formOption.getValidate(); + if (validate != null + && validate.getMessage().startsWith(FormLocale.I18N_PREFIX)) { + String message = + validate.getMessage().replace(FormLocale.I18N_PREFIX, ""); + validateOneI18nOption( + locales, + formOption.getLabel(), + "validateMessage", + message, + errorMessageList); + } + }); + return errorMessageList; + } + + private static void validateOneI18nOption( + FormLocale locale, + @NonNull String formOptionLabel, + @NonNull String formOptionName, + @NonNull String key, + @NonNull List errorMessageList) { + if (locale == null || !locale.getEnUS().containsKey(key)) { + errorMessageList.add( + String.format( + "FormOption[%s] used i18n %s[%s] can not found in FormStructure.locales en_US", + formOptionLabel, formOptionName, key)); + } + + if (locale == null || !locale.getZhCN().containsKey(key)) { + errorMessageList.add( + String.format( + "FormOption[%s] used i18n %s[%s] can not found in FormStructure.locales zh_CN", + formOptionLabel, formOptionName, key)); + } + } + + private static List validateShow(@NonNull FormStructure formStructure) { + List errorMessageList = new ArrayList(); + // Find all select options + List allFields = + formStructure.getForms().stream() + .map(formOption -> formOption.getField()) + .collect(Collectors.toList()); + formStructure + .getForms() + .forEach( + formOption -> { + Map show = formOption.getShow(); + if (show == null) { + return; + } + + String field = show.get("field").toString(); + if (allFields == null || !allFields.contains(field)) { + errorMessageList.add( + String.format( + "FormOption[%s] used show field[%s] can not found in form options", + formOption.getLabel(), field)); + } + }); + + return errorMessageList; + } + + private static List validateUnionNonEmpty(@NonNull FormStructure formStructure) { + List errorMessageList = new ArrayList(); + Map> unionMap = new HashMap<>(); + // find all union-non-empty options + formStructure + .getForms() + .forEach( + formOption -> { + if (formOption.getValidate() != null + && formOption.getValidate() instanceof UnionNonEmptyValidate) { + unionMap.put( + formOption.getField(), + ((UnionNonEmptyValidate) formOption.getValidate()) + .getFields()); + } + }); + + unionMap.forEach( + (k, v) -> { + if (v == null || !v.contains(k)) { + errorMessageList.add( + String.format( + "UnionNonEmptyValidate Option field[%s] must in validate union field list", + k)); + } + + if (v != null) { + v.forEach( + field -> { + if (!unionMap.keySet().contains(field)) { + errorMessageList.add( + String.format( + "UnionNonEmptyValidate Option field[%s] , validate union field[%s] can not found in form options", + k, field)); + } + }); + } + }); + + return errorMessageList; + } + + private static List validateMutuallyExclusive(@NonNull FormStructure formStructure) { + List errorMessageList = new ArrayList(); + Map> exclusiveMap = new HashMap<>(); + // find all mutually-exclusive options + formStructure + .getForms() + .forEach( + formOption -> { + if (formOption.getValidate() != null + && formOption.getValidate() instanceof MutuallyExclusiveValidate) { + exclusiveMap.put( + formOption.getField(), + ((MutuallyExclusiveValidate) formOption.getValidate()) + .getFields()); + } + }); + + exclusiveMap.forEach( + (k, v) -> { + if (v == null || !v.contains(k)) { + errorMessageList.add( + String.format( + "MutuallyExclusiveValidate Option field[%s] must in validate field list", + k)); + } + + if (v != null) { + v.forEach( + field -> { + if (!exclusiveMap.keySet().contains(field)) { + errorMessageList.add( + String.format( + "MutuallyExclusiveValidate Option field[%s] , validate field[%s] can not found in form options", + k, field)); + } + }); + } + }); + + return errorMessageList; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/StaticSelectOption.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/StaticSelectOption.java new file mode 100644 index 000000000..e70f4a24c --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/StaticSelectOption.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +public class StaticSelectOption extends AbstractFormSelectOption { + + @Getter + @Setter + private List options = new ArrayList<>(); + + public StaticSelectOption( + @NonNull List options, @NonNull String label, @NonNull String field) { + super(label, field); + this.options = options; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/exception/FormStructureValidateException.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/exception/FormStructureValidateException.java new file mode 100644 index 000000000..02804e32d --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/exception/FormStructureValidateException.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms.exception; + +import lombok.NonNull; + +import java.util.List; + +public class FormStructureValidateException extends RuntimeException { + + public FormStructureValidateException( + @NonNull String formName, @NonNull List errorList, @NonNull Throwable e) { + super(String.format("Form: %s, validate error - %s", formName, errorList), e); + } + + public FormStructureValidateException( + @NonNull String formName, @NonNull List errorList) { + super(String.format("Form: %s, validate error - %s", formName, errorList)); + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/AbstractValidate.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/AbstractValidate.java new file mode 100644 index 000000000..f5576611c --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/AbstractValidate.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms.validate; + +import org.apache.seatunnel.app.dynamicforms.FormLocale; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.Getter; +import lombok.NonNull; + +import java.util.Arrays; +import java.util.List; + +@Data +public class AbstractValidate { + private final List trigger = Arrays.asList("input", "blur"); + + // support i18n + private String message = "required"; + + public enum RequiredType { + @JsonProperty("non-empty") + NON_EMPTY("non-empty"), + + @JsonProperty("union-non-empty") + UNION_NON_EMPTY("union-non-empty"), + + @JsonProperty("mutually-exclusive") + MUTUALLY_EXCLUSIVE("mutually-exclusive"); + + @Getter + private String type; + + RequiredType(String type) { + this.type = type; + } + } + + public T withMessage(@NonNull String message) { + this.message = message; + return (T) this; + } + + public T withI18nMessage(@NonNull String message) { + this.message = FormLocale.I18N_PREFIX + message; + return (T) this; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/MutuallyExclusiveValidate.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/MutuallyExclusiveValidate.java new file mode 100644 index 000000000..a223d9dfd --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/MutuallyExclusiveValidate.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms.validate; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.NonNull; + +import java.util.List; + +@Data +public class MutuallyExclusiveValidate extends AbstractValidate { + private final boolean required = false; + private List fields; + + @JsonProperty("type") + private final RequiredType requiredType = RequiredType.MUTUALLY_EXCLUSIVE; + + public MutuallyExclusiveValidate(@NonNull List fields) { + checkArgument(fields.size() > 0); + this.fields = fields; + this.withMessage("parameters:" + fields + ", only one can be set"); + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/NonEmptyValidate.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/NonEmptyValidate.java new file mode 100644 index 000000000..0ee27cbb5 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/NonEmptyValidate.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms.validate; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; + +@Data +public class NonEmptyValidate extends AbstractValidate { + private final boolean required = true; + + @JsonProperty("type") + private final RequiredType requiredType = RequiredType.NON_EMPTY; +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/UnionNonEmptyValidate.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/UnionNonEmptyValidate.java new file mode 100644 index 000000000..8328459d9 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/UnionNonEmptyValidate.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms.validate; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; +import lombok.NonNull; + +import java.util.List; + +@Data +public class UnionNonEmptyValidate extends AbstractValidate { + private final boolean required = false; + private List fields; + + @JsonProperty("type") + private final RequiredType requiredType = RequiredType.UNION_NON_EMPTY; + + public UnionNonEmptyValidate(@NonNull List fields) { + checkArgument(fields.size() > 0); + this.fields = fields; + this.withMessage( + "parameters:" + + fields + + " if any of these parameters is set, other parameters must also be set"); + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/ValidateBuilder.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/ValidateBuilder.java new file mode 100644 index 000000000..53a6e94f6 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/validate/ValidateBuilder.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms.validate; + +import lombok.NonNull; + +import java.util.ArrayList; +import java.util.List; + +public class ValidateBuilder { + + public static ValidateBuilder builder() { + return new ValidateBuilder(); + } + + public NonEmptyValidateBuilder nonEmptyValidateBuilder() { + return new NonEmptyValidateBuilder(); + } + + public UnionNonEmptyValidateBuilder unionNonEmptyValidateBuilder() { + return new UnionNonEmptyValidateBuilder(); + } + + public MutuallyExclusiveValidateBuilder mutuallyExclusiveValidateBuilder() { + return new MutuallyExclusiveValidateBuilder(); + } + + public static class NonEmptyValidateBuilder { + public NonEmptyValidate nonEmptyValidate() { + return new NonEmptyValidate(); + } + } + + public static class UnionNonEmptyValidateBuilder { + private List fields = new ArrayList<>(); + + public UnionNonEmptyValidateBuilder fields(@NonNull String... fields) { + for (String field : fields) { + this.fields.add(field); + } + return this; + } + + public UnionNonEmptyValidate unionNonEmptyValidate() { + return new UnionNonEmptyValidate(fields); + } + } + + public static class MutuallyExclusiveValidateBuilder { + private List fields = new ArrayList<>(); + + public MutuallyExclusiveValidateBuilder fields(@NonNull String... fields) { + for (String field : fields) { + this.fields.add(field); + } + return this; + } + + public MutuallyExclusiveValidate mutuallyExclusiveValidate() { + return new MutuallyExclusiveValidate(fields); + } + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilderTest.java b/seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilderTest.java new file mode 100644 index 000000000..cbea72a85 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/FormStructureBuilderTest.java @@ -0,0 +1,329 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import org.apache.seatunnel.app.dynamicforms.exception.FormStructureValidateException; +import org.apache.seatunnel.app.dynamicforms.validate.ValidateBuilder; +import org.apache.seatunnel.common.utils.FileUtils; +import org.apache.seatunnel.common.utils.JsonUtils; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.nio.file.Paths; +import java.util.Arrays; + +public class FormStructureBuilderTest { + + @Test + public void testFormStructureBuild() { + FormLocale locale = new FormLocale(); + locale.addZhCN("name_password_union_required", "all name and password are required") + .addZhCN("username", "username") + .addEnUS("name_password_union_required", "all name and password are required") + .addEnUS("username", "username"); + + FormInputOption nameOption = + (FormInputOption) + FormOptionBuilder.builder() + .withI18nLabel("username") + .withField("username") + .inputOptionBuilder() + .formTextInputOption() + .withDescription("username") + .withClearable() + .withPlaceholder("username") + .withShow("checkType", Arrays.asList("nameAndPassword")) + .withValidate( + ValidateBuilder.builder() + .unionNonEmptyValidateBuilder() + .fields("username", "password") + .unionNonEmptyValidate() + .withI18nMessage("name_password_union_required")); + + FormInputOption passwordOption = + (FormInputOption) + FormOptionBuilder.builder() + .withLabel("password") + .withField("password") + .inputOptionBuilder() + .formPasswordInputOption() + .withDescription("password") + .withPlaceholder("password") + .withShow("checkType", Arrays.asList("nameAndPassword")) + .withValidate( + ValidateBuilder.builder() + .unionNonEmptyValidateBuilder() + .fields("username", "password") + .unionNonEmptyValidate() + .withI18nMessage("name_password_union_required")); + + FormInputOption textAreaOption = + (FormInputOption) + FormOptionBuilder.builder() + .withLabel("content") + .withField("context") + .inputOptionBuilder() + .formTextareaInputOption() + .withClearable() + .withDescription("content"); + + StaticSelectOption checkTypeOption = + (StaticSelectOption) + FormOptionBuilder.builder() + .withLabel("checkType") + .withField("checkType") + .staticSelectOptionBuilder() + .addSelectOptions( + new ImmutablePair("no", "no"), + new ImmutablePair("nameAndPassword", "nameAndPassword")) + .formStaticSelectOption() + .withClearable() + .withDefaultValue("no") + .withDescription("check type") + .withValidate( + ValidateBuilder.builder() + .nonEmptyValidateBuilder() + .nonEmptyValidate()); + + DynamicSelectOption cityOption = + (DynamicSelectOption) + FormOptionBuilder.builder() + .withField("city") + .withLabel("city") + .dynamicSelectOptionBuilder() + .withSelectApi("getCity") + .formDynamicSelectOption() + .withDescription("city") + .withValidate( + ValidateBuilder.builder() + .nonEmptyValidateBuilder() + .nonEmptyValidate()); + + AbstractFormOption exclusive1 = + FormOptionBuilder.builder() + .withField("exclusive1") + .withLabel("exclusive1") + .inputOptionBuilder() + .formTextInputOption() + .withValidate( + ValidateBuilder.builder() + .mutuallyExclusiveValidateBuilder() + .fields("exclusive1", "exclusive2") + .mutuallyExclusiveValidate()); + + AbstractFormOption exclusive2 = + FormOptionBuilder.builder() + .withField("exclusive2") + .withLabel("exclusive2") + .inputOptionBuilder() + .formTextInputOption() + .withValidate( + ValidateBuilder.builder() + .mutuallyExclusiveValidateBuilder() + .fields("exclusive1", "exclusive2") + .mutuallyExclusiveValidate()); + + FormStructure testForm = + FormStructure.builder() + .name("testForm") + .addFormOption( + nameOption, + passwordOption, + textAreaOption, + checkTypeOption, + cityOption, + exclusive1, + exclusive2) + .withLocale(locale) + .addApi("getCity", "/api/get_city", FormStructure.HttpMethod.GET) + .build(); + + String s = JsonUtils.toJsonString(testForm); + String templateFilePath = TestUtils.getResource("test_form.json"); + String result = FileUtils.readFileToStr(Paths.get(templateFilePath)); + Assertions.assertEquals(result, s); + } + + @Test + public void testFormStructureValidate() { + FormLocale locale = new FormLocale(); + locale.addZhCN("name_password_union_required", "all name and password are required") + .addEnUS("name_password_union_required", "all name and password are required") + .addEnUS("username", "username"); + + FormInputOption nameOption = + (FormInputOption) + FormOptionBuilder.builder() + .withI18nLabel("username") + .withField("username") + .inputOptionBuilder() + .formTextInputOption() + .withDescription("username") + .withClearable() + .withPlaceholder("username") + .withShow("checkType1", Arrays.asList("nameAndPassword")) + .withValidate( + ValidateBuilder.builder() + .unionNonEmptyValidateBuilder() + .fields("user", "password") + .unionNonEmptyValidate() + .withI18nMessage("name_password_union_required")); + + FormInputOption passwordOption = + (FormInputOption) + FormOptionBuilder.builder() + .withLabel("password") + .withField("password") + .inputOptionBuilder() + .formPasswordInputOption() + .withDescription("password") + .withPlaceholder("password") + .withShow("checkType", Arrays.asList("nameAndPassword")) + .withValidate( + ValidateBuilder.builder() + .unionNonEmptyValidateBuilder() + .fields("username", "password") + .unionNonEmptyValidate() + .withI18nMessage("name_password_union_required")); + + FormInputOption textAreaOption = + (FormInputOption) + FormOptionBuilder.builder() + .withLabel("content") + .withField("context") + .inputOptionBuilder() + .formTextareaInputOption() + .withClearable() + .withDescription("content"); + + StaticSelectOption checkTypeOption = + (StaticSelectOption) + FormOptionBuilder.builder() + .withLabel("checkType") + .withField("checkType") + .staticSelectOptionBuilder() + .addSelectOptions( + new ImmutablePair("no", "no"), + new ImmutablePair("nameAndPassword", "nameAndPassword")) + .formStaticSelectOption() + .withClearable() + .withDefaultValue("no") + .withDescription("check type") + .withValidate( + ValidateBuilder.builder() + .nonEmptyValidateBuilder() + .nonEmptyValidate()); + + DynamicSelectOption cityOption = + (DynamicSelectOption) + FormOptionBuilder.builder() + .withField("city") + .withLabel("city") + .dynamicSelectOptionBuilder() + .withSelectApi("getCity") + .formDynamicSelectOption() + .withDescription("city") + .withValidate( + ValidateBuilder.builder() + .nonEmptyValidateBuilder() + .nonEmptyValidate()); + + AbstractFormOption exclusive1 = + FormOptionBuilder.builder() + .withField("exclusive1") + .withLabel("exclusive1") + .inputOptionBuilder() + .formTextInputOption() + .withValidate( + ValidateBuilder.builder() + .mutuallyExclusiveValidateBuilder() + .fields("exclusive1", "exclusive2") + .mutuallyExclusiveValidate()); + + AbstractFormOption exclusive2 = + FormOptionBuilder.builder() + .withField("exclusive2") + .withLabel("exclusive2") + .inputOptionBuilder() + .formTextInputOption() + .withValidate( + ValidateBuilder.builder() + .mutuallyExclusiveValidateBuilder() + .fields("exclusive1", "exclusive2") + .mutuallyExclusiveValidate()); + + AbstractFormOption exclusive3 = + FormOptionBuilder.builder() + .withField("exclusive3") + .withLabel("exclusive3") + .inputOptionBuilder() + .formTextInputOption() + .withValidate( + ValidateBuilder.builder() + .mutuallyExclusiveValidateBuilder() + .fields("exclusive1", "exclusive2") + .mutuallyExclusiveValidate()); + + AbstractFormOption exclusive4 = + FormOptionBuilder.builder() + .withField("exclusive4") + .withLabel("exclusive4") + .inputOptionBuilder() + .formTextInputOption() + .withValidate( + ValidateBuilder.builder() + .mutuallyExclusiveValidateBuilder() + .fields("exclusive1", "exclusive4", "exclusive5") + .mutuallyExclusiveValidate()); + + String error = ""; + try { + FormStructure testForm = + FormStructure.builder() + .name("testForm") + .addFormOption( + nameOption, + passwordOption, + textAreaOption, + checkTypeOption, + cityOption, + exclusive1, + exclusive3, + exclusive2, + exclusive4) + .withLocale(locale) + .addApi("getCity1", "/api/get_city", FormStructure.HttpMethod.GET) + .build(); + } catch (FormStructureValidateException e) { + error = e.getMessage(); + } + + String result = + "Form: testForm, validate error - " + + "[DynamicSelectOption[city] used api[getCity] can not found in FormStructure.apis, " + + "FormOption[i18n.username] used i18n label[username] can not found in FormStructure.locales zh_CN, " + + "FormOption[i18n.username] used show field[checkType1] can not found in form options, " + + "UnionNonEmptyValidate Option field[username] must in validate union field list, " + + "UnionNonEmptyValidate Option field[username] , validate union field[user] can not found in form options, " + + "MutuallyExclusiveValidate Option field[exclusive3] must in validate field list, " + + "MutuallyExclusiveValidate Option field[exclusive4] , validate field[exclusive5] can not found in form options]"; + Assertions.assertEquals(result, error); + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/TestUtils.java b/seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/TestUtils.java new file mode 100644 index 000000000..e0f94182e --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/test/java/org/apache/seatunnel/app/dynamicforms/TestUtils.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.dynamicforms; + +import java.io.File; + +public class TestUtils { + public static String getResource(String confFile) { + return System.getProperty("user.dir") + + File.separator + + "src" + + File.separator + + "test" + + File.separator + + "resources" + + File.separator + + confFile; + } +} diff --git a/seatunnel-server/seatunnel-dynamicform/src/test/resources/test_form.json b/seatunnel-server/seatunnel-dynamicform/src/test/resources/test_form.json new file mode 100644 index 000000000..8044981b7 --- /dev/null +++ b/seatunnel-server/seatunnel-dynamicform/src/test/resources/test_form.json @@ -0,0 +1 @@ +{"name":"testForm","locales":{"zh_CN":{"name_password_union_required":"all name and password are required","username":"username"},"en_US":{"name_password_union_required":"all name and password are required","username":"username"}},"apis":{"getCity":{"method":"get","url":"/api/get_city"}},"forms":[{"label":"i18n.username","field":"username","defaultValue":null,"description":"username","clearable":true,"show":{"field":"checkType","value":["nameAndPassword"]},"placeholder":"username","validate":{"trigger":["input","blur"],"message":"i18n.name_password_union_required","required":false,"fields":["username","password"],"type":"union-non-empty"},"inputType":"text","type":"input"},{"label":"password","field":"password","defaultValue":null,"description":"password","clearable":false,"show":{"field":"checkType","value":["nameAndPassword"]},"placeholder":"password","validate":{"trigger":["input","blur"],"message":"i18n.name_password_union_required","required":false,"fields":["username","password"],"type":"union-non-empty"},"inputType":"password","type":"input"},{"label":"content","field":"context","defaultValue":null,"description":"content","clearable":true,"placeholder":"","inputType":"textarea","type":"input"},{"label":"checkType","field":"checkType","defaultValue":"no","description":"check type","clearable":true,"placeholder":"","validate":{"trigger":["input","blur"],"message":"required","required":true,"type":"non-empty"},"options":[{"label":"no","value":"no"},{"label":"nameAndPassword","value":"nameAndPassword"}],"type":"select"},{"label":"city","field":"city","defaultValue":null,"description":"city","clearable":false,"placeholder":"","validate":{"trigger":["input","blur"],"message":"required","required":true,"type":"non-empty"},"api":"getCity","type":"select"},{"label":"exclusive1","field":"exclusive1","defaultValue":null,"description":"","clearable":false,"placeholder":"","validate":{"trigger":["input","blur"],"message":"parameters:[exclusive1, exclusive2], only one can be set","required":false,"fields":["exclusive1","exclusive2"],"type":"mutually-exclusive"},"inputType":"text","type":"input"},{"label":"exclusive2","field":"exclusive2","defaultValue":null,"description":"","clearable":false,"placeholder":"","validate":{"trigger":["input","blur"],"message":"parameters:[exclusive1, exclusive2], only one can be set","required":false,"fields":["exclusive1","exclusive2"],"type":"mutually-exclusive"},"inputType":"text","type":"input"}]} \ No newline at end of file diff --git a/seatunnel-server/seatunnel-server-common/pom.xml b/seatunnel-server/seatunnel-server-common/pom.xml index 6eb22aac5..35bf91c8e 100644 --- a/seatunnel-server/seatunnel-server-common/pom.xml +++ b/seatunnel-server/seatunnel-server-common/pom.xml @@ -31,9 +31,10 @@ - com.fasterxml.jackson.core - jackson-databind - provided + org.apache.seatunnel + seatunnel-jackson + ${seatunnel-framework.version} + optional \ No newline at end of file diff --git a/seatunnel-ui/src/router/routes.ts b/seatunnel-ui/src/router/routes.ts index 64eac4680..19fdd67e0 100644 --- a/seatunnel-ui/src/router/routes.ts +++ b/seatunnel-ui/src/router/routes.ts @@ -28,7 +28,7 @@ const components: { [key: string]: Component } = utils.mapping(modules) const basePage: RouteRecordRaw[] = [{ path: '/', - redirect: { name: 'data-pipes' } + redirect: { name: 'login' } }, dataPipes, jobs, tasks, userManage] diff --git a/tools/dependencies/known-dependencies.txt b/tools/dependencies/known-dependencies.txt index 055e93898..5f7dfbe3b 100644 --- a/tools/dependencies/known-dependencies.txt +++ b/tools/dependencies/known-dependencies.txt @@ -50,9 +50,10 @@ mybatis-plus-boot-starter-3.5.3.1.jar mybatis-plus-core-3.5.3.1.jar mybatis-plus-extension-3.5.3.1.jar scala-library-2.11.12.jar -seatunnel-common-2.1.3.jar -seatunnel-config-base-2.1.1.jar -seatunnel-config-shade-2.1.1.jar +seatunnel-common-2.3.1.jar +seatunnel-config-base-2.3.1.jar +seatunnel-config-shade-2.3.1.jar +seatunnel-jackson-2.3.1-optional.jar slf4j-api-1.7.25.jar snakeyaml-1.29.jar spring-aop-5.3.20.jar From 4dee2faacb2ee644ffaf66b442e589696b52991e Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 11 May 2023 04:29:41 +0800 Subject: [PATCH 07/28] [Improve][Pom] use revision (#53) --- pom.xml | 3 ++- seatunnel-server/pom.xml | 2 +- seatunnel-server/seatunnel-app/pom.xml | 2 +- seatunnel-server/seatunnel-dynamicform/pom.xml | 2 +- seatunnel-server/seatunnel-scheduler/pom.xml | 2 +- .../seatunnel-scheduler-dolphinscheduler/pom.xml | 2 +- seatunnel-server/seatunnel-server-common/pom.xml | 2 +- seatunnel-server/seatunnel-spi/pom.xml | 2 +- seatunnel-web-dist/pom.xml | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 070562e0f..6097e7c8e 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ org.apache.seatunnel seatunnel-web pom - 1.0.0-SNAPSHOT + ${revision} SeaTunnel @@ -76,6 +76,7 @@ + 1.0.0-SNAPSHOT ${java.version} ${java.version} 1.8 diff --git a/seatunnel-server/pom.xml b/seatunnel-server/pom.xml index d6bd26dfc..a71c78e3c 100644 --- a/seatunnel-server/pom.xml +++ b/seatunnel-server/pom.xml @@ -19,7 +19,7 @@ seatunnel-web org.apache.seatunnel - 1.0.0-SNAPSHOT + ${revision} 4.0.0 diff --git a/seatunnel-server/seatunnel-app/pom.xml b/seatunnel-server/seatunnel-app/pom.xml index 0e94b6549..ede117405 100644 --- a/seatunnel-server/seatunnel-app/pom.xml +++ b/seatunnel-server/seatunnel-app/pom.xml @@ -19,7 +19,7 @@ seatunnel-server org.apache.seatunnel - 1.0.0-SNAPSHOT + ${revision} 4.0.0 diff --git a/seatunnel-server/seatunnel-dynamicform/pom.xml b/seatunnel-server/seatunnel-dynamicform/pom.xml index 36376f75d..d5975df82 100644 --- a/seatunnel-server/seatunnel-dynamicform/pom.xml +++ b/seatunnel-server/seatunnel-dynamicform/pom.xml @@ -19,7 +19,7 @@ seatunnel-server org.apache.seatunnel - 1.0.0-SNAPSHOT + ${revision} seatunnel-dynamicform diff --git a/seatunnel-server/seatunnel-scheduler/pom.xml b/seatunnel-server/seatunnel-scheduler/pom.xml index 80c9aa44e..682437b8a 100644 --- a/seatunnel-server/seatunnel-scheduler/pom.xml +++ b/seatunnel-server/seatunnel-scheduler/pom.xml @@ -19,7 +19,7 @@ seatunnel-server org.apache.seatunnel - 1.0.0-SNAPSHOT + ${revision} 4.0.0 diff --git a/seatunnel-server/seatunnel-scheduler/seatunnel-scheduler-dolphinscheduler/pom.xml b/seatunnel-server/seatunnel-scheduler/seatunnel-scheduler-dolphinscheduler/pom.xml index 099115ad5..18d0e7be4 100644 --- a/seatunnel-server/seatunnel-scheduler/seatunnel-scheduler-dolphinscheduler/pom.xml +++ b/seatunnel-server/seatunnel-scheduler/seatunnel-scheduler-dolphinscheduler/pom.xml @@ -19,7 +19,7 @@ seatunnel-scheduler org.apache.seatunnel - 1.0.0-SNAPSHOT + ${revision} 4.0.0 diff --git a/seatunnel-server/seatunnel-server-common/pom.xml b/seatunnel-server/seatunnel-server-common/pom.xml index 35bf91c8e..e1c34efec 100644 --- a/seatunnel-server/seatunnel-server-common/pom.xml +++ b/seatunnel-server/seatunnel-server-common/pom.xml @@ -19,7 +19,7 @@ seatunnel-server org.apache.seatunnel - 1.0.0-SNAPSHOT + ${revision} 4.0.0 diff --git a/seatunnel-server/seatunnel-spi/pom.xml b/seatunnel-server/seatunnel-spi/pom.xml index 614c90250..723f3c420 100644 --- a/seatunnel-server/seatunnel-spi/pom.xml +++ b/seatunnel-server/seatunnel-spi/pom.xml @@ -19,7 +19,7 @@ seatunnel-server org.apache.seatunnel - 1.0.0-SNAPSHOT + ${revision} 4.0.0 diff --git a/seatunnel-web-dist/pom.xml b/seatunnel-web-dist/pom.xml index 6917980a8..4a8402981 100644 --- a/seatunnel-web-dist/pom.xml +++ b/seatunnel-web-dist/pom.xml @@ -23,7 +23,7 @@ org.apache.seatunnel seatunnel-web - 1.0.0-SNAPSHOT + ${revision} 4.0.0 From 4398dc88c2a2f6ed957ef3a9f400c7a66a616a43 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 15 May 2023 14:37:41 +0800 Subject: [PATCH 08/28] add datasource module (#54) --- README.md | 2 +- build.sh | 2 +- pom.xml | 100 ++- seatunnel-datasource/pom.xml | 40 + .../seatunnel-datasource-client/pom.xml | 71 ++ .../datasource/AbstractDataSourceClient.java | 135 ++++ .../datasource/DataSourceClient.java | 28 + .../datasource/annotation/ThreadSafe.java | 29 + .../exception/DataSourceSDKException.java | 37 + .../datasource/request/PageQueryRequest.java | 42 + .../request/VirtualTableCreateRequest.java | 40 + .../request/VirtualTableFieldRequest.java | 42 + .../request/VirtualTableUpdateRequest.java | 36 + .../datasource/response/BaseResponse.java | 38 + .../response/DataSourceDetailResponse.java | 38 + .../datasource/response/PageDataResponse.java | 32 + .../response/PageResultResponse.java | 36 + .../response/VirtualTableDetailResponse.java | 42 + .../response/VirtualTableFieldResponse.java | 42 + .../response/VirtualTableResponse.java | 36 + .../datasource/service/DataSourceService.java | 116 +++ .../s3/S3DatasourceChannelTest.java | 55 ++ .../datasource/DataSourceClientTest.java | 48 ++ .../datasource-all/pom.xml | 112 +++ .../datasource-elasticsearch/pom.xml | 62 ++ .../ElasticSearchDataSourceChannel.java | 135 ++++ .../ElasticSearchDataSourceConfig.java | 36 + .../ElasticSearchDataSourceFactory.java | 48 ++ .../ElasticSearchOptionRule.java | 109 +++ .../client/ElasticsearchClusterInfo.java | 30 + .../elasticsearch/client/EsRestClient.java | 371 +++++++++ .../client/ResponseException.java | 45 ++ .../plugin/elasticsearch/client/SSLUtils.java | 165 ++++ .../ElasticSearchDataSourceChannelTest.java | 114 +++ .../ElasticSearchDataSourceFactoryTest.java | 43 + .../datasource-jdbc-clickhouse/pom.xml | 59 ++ .../jdbc/ClickhouseDataSourceConfig.java | 59 ++ .../jdbc/ClickhouseJdbcDataSourceChannel.java | 177 ++++ .../jdbc/ClickhouseJdbcDataSourceFactory.java | 48 ++ .../clickhouse/jdbc/ClickhouseOptionRule.java | 68 ++ .../datasource-jdbc-hive/pom.xml | 35 + .../plugin/hive/jdbc/HiveJdbcConstants.java | 29 + .../hive/jdbc/HiveJdbcDataSourceChannel.java | 215 +++++ .../hive/jdbc/HiveJdbcDataSourceFactory.java | 53 ++ .../plugin/hive/jdbc/HiveJdbcOptionRule.java | 42 + .../datasource-jdbc-mysql/pom.xml | 59 ++ .../mysql/jdbc/MysqlDataSourceConfig.java | 51 ++ .../jdbc/MysqlJdbcDataSourceChannel.java | 182 +++++ .../jdbc/MysqlJdbcDataSourceFactory.java | 48 ++ .../plugin/mysql/jdbc/MysqlOptionRule.java | 69 ++ .../datasource-jdbc-oracle/pom.xml | 59 ++ .../oracle/jdbc/OracleDataSourceChannel.java | 174 ++++ .../oracle/jdbc/OracleDataSourceConfig.java | 53 ++ .../jdbc/OracleJdbcDataSourceFactory.java | 47 ++ .../plugin/oracle/jdbc/OracleOptionRule.java | 67 ++ .../datasource-jdbc-postgresql/pom.xml | 58 ++ .../jdbc/PostgresqlDataSourceChannel.java | 184 +++++ .../jdbc/PostgresqlDataSourceConfig.java | 62 ++ .../jdbc/PostgresqlDataSourceFactory.java | 46 ++ .../postgresql/jdbc/PostgresqlOptionRule.java | 69 ++ .../datasource-jdbc-redshift/pom.xml | 59 ++ .../jdbc/RedshiftDataSourceChannel.java | 180 +++++ .../jdbc/RedshiftDataSourceConfig.java | 62 ++ .../jdbc/RedshiftDataSourceFactory.java | 48 ++ .../redshift/jdbc/RedshiftOptionRule.java | 69 ++ .../datasource-jdbc-sqlserver/pom.xml | 59 ++ .../jdbc/SqlServerDataSourceChannel.java | 175 ++++ .../jdbc/SqlServerDataSourceConfig.java | 60 ++ .../jdbc/SqlServerDataSourceFactory.java | 48 ++ .../sqlserver/jdbc/SqlServerOptionRule.java | 68 ++ .../datasource-jdbc-starrocks/pom.xml | 59 ++ .../jdbc/StarRocksDataSourceConfig.java | 53 ++ .../jdbc/StarRocksJdbcDataSourceChannel.java | 175 ++++ .../jdbc/StarRocksJdbcDataSourceFactory.java | 46 ++ .../starrocks/jdbc/StarRocksOptionRule.java | 69 ++ .../datasource-jdbc-tidb/pom.xml | 36 + .../tidb/jdbc/TidbDataSourceConfig.java | 51 ++ .../tidb/jdbc/TidbJdbcDataSourceChannel.java | 182 +++++ .../tidb/jdbc/TidbJdbcDataSourceFactory.java | 48 ++ .../plugin/tidb/jdbc/TidbOptionRule.java | 69 ++ .../datasource-kafka/pom.xml | 56 ++ .../plugin/kafka/KafkaDataSourceChannel.java | 115 +++ .../plugin/kafka/KafkaDataSourceFactory.java | 58 ++ .../plugin/kafka/KafkaOptionRule.java | 67 ++ .../plugin/kafka/KafkaRequestParamsUtils.java | 55 ++ .../kafka/KafkaDataSourceChannelTest.java | 106 +++ .../kafka/KafkaRequestParamsUtilsTest.java | 66 ++ .../datasource-mysql-cdc/pom.xml | 57 ++ .../cdc/mysql/MysqlCDCDataSourceChannel.java | 249 ++++++ .../cdc/mysql/MysqlCDCDataSourceConfig.java | 34 + .../cdc/mysql/MysqlCDCDataSourceFactory.java | 46 ++ .../plugin/cdc/mysql/MysqlCDCOptionRule.java | 74 ++ .../datasource-plugins-api/pom.xml | 37 + .../plugin/api/DataSourceChannel.java | 83 ++ .../plugin/api/DataSourceFactory.java | 29 + .../plugin/api/DataSourcePluginException.java | 37 + .../plugin/api/DataSourcePluginInfo.java | 53 ++ .../plugin/api/DatasourcePluginTypeEnum.java | 53 ++ .../plugin/api/common/ParamtersUtils.java | 40 + .../plugin/api/model/TableField.java | 40 + .../plugin/api/utils/JdbcUtils.java | 43 + .../plugin/api/common/ParamtersUtilsTest.java | 49 ++ .../datasource-s3-redshift/pom.xml | 67 ++ .../redshift/s3/HadoopS3AConfiguration.java | 100 +++ .../s3/S3RedshiftDataSourceChannel.java | 268 +++++++ .../s3/S3RedshiftDataSourceFactory.java | 56 ++ .../redshift/s3/S3RedshiftOptionRule.java | 191 +++++ .../datasource-s3/pom.xml | 68 ++ .../plugin/s3/HadoopS3AConfiguration.java | 95 +++ .../plugin/s3/S3DataSourceFactory.java | 58 ++ .../plugin/s3/S3DatasourceChannel.java | 89 ++ .../datasource/plugin/s3/S3OptionRule.java | 166 ++++ .../datasource-sqlserver-cdc/pom.xml | 52 ++ .../SqlServerCDCDataSourceChannel.java | 231 ++++++ .../SqlServerCDCDataSourceConfig.java | 34 + .../SqlServerCDCDataSourceFactory.java | 47 ++ .../cdc/sqlserver/SqlServerCDCOptionRule.java | 73 ++ .../TestSqlServerCDCDataSourceChannel.java | 48 ++ .../datasource-starrocks/pom.xml | 57 ++ .../plugin/starrocks/StarRocksCatalog.java | 224 ++++++ .../starrocks/StarRocksDataSourceChannel.java | 147 ++++ .../starrocks/StarRocksDataSourceConfig.java | 34 + .../starrocks/StarRocksDataSourceFactory.java | 45 ++ .../plugin/starrocks/StarRocksOptionRule.java | 74 ++ .../seatunnel-datasource-plugins/pom.xml | 53 ++ seatunnel-server/pom.xml | 5 + seatunnel-server/seatunnel-app/pom.xml | 154 +--- .../src/main/assembly/seatunnel-web.xml | 64 -- .../src/main/bin/seatunnel-backend-daemon.sh | 2 +- .../app/controller/AuthController.java | 2 +- .../src/main/resources/application.yml | 2 +- seatunnel-web-dist/pom.xml | 349 +++++++- .../release-docs/{binary => }/LICENSE | 17 +- .../release-docs/{binary => }/NOTICE | 0 .../licenses/LICENSE-Jetty-9.x.txt | 0 .../{binary => }/licenses/LICENSE-aspectj.txt | 0 .../licenses/LICENSE-cron-utils.txt | 202 +++++ .../{binary => }/licenses/LICENSE-h2.txt | 0 .../{binary => }/licenses/LICENSE-jakarta.txt | 0 .../licenses/LICENSE-javassist.html | 373 +++++++++ .../licenses/LICENSE-javax-annotation.txt | 0 .../licenses/LICENSE-javax.el.txt | 759 ++++++++++++++++++ .../{binary => }/licenses/LICENSE-jsoup.txt | 0 .../{binary => }/licenses/LICENSE-logback.txt | 0 .../licenses/LICENSE-mapstruct.txt | 0 .../{binary => }/licenses/LICENSE-slf4j.txt | 0 .../licenses/LICENSE-spring-plugin.txt | 0 .../licenses/LICENSE-swagger-core.txt | 0 .../src/main/assembly/seatunnel-web-ci.xml | 117 +++ .../src/main/assembly/seatunnel-web.xml | 48 +- tools/dependencies/checkLicense.sh | 2 +- tools/dependencies/known-dependencies.txt | 3 +- 152 files changed, 11582 insertions(+), 227 deletions(-) create mode 100644 seatunnel-datasource/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-client/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/DataSourceClient.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/exception/DataSourceSDKException.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/PageQueryRequest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableCreateRequest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableFieldRequest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableUpdateRequest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/BaseResponse.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/DataSourceDetailResponse.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageDataResponse.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageResultResponse.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableDetailResponse.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableFieldResponse.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableResponse.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-all/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ElasticsearchClusterInfo.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ResponseException.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcConstants.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleJdbcDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginException.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DatasourcePluginTypeEnum.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/model/TableField.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/utils/JdbcUtils.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/test/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtilsTest.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3OptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/pom.xml create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceConfig.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceFactory.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksOptionRule.java create mode 100644 seatunnel-datasource/seatunnel-datasource-plugins/pom.xml delete mode 100644 seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml rename seatunnel-web-dist/release-docs/{binary => }/LICENSE (96%) rename seatunnel-web-dist/release-docs/{binary => }/NOTICE (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-Jetty-9.x.txt (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-aspectj.txt (100%) create mode 100644 seatunnel-web-dist/release-docs/licenses/LICENSE-cron-utils.txt rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-h2.txt (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-jakarta.txt (100%) create mode 100644 seatunnel-web-dist/release-docs/licenses/LICENSE-javassist.html rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-javax-annotation.txt (100%) create mode 100644 seatunnel-web-dist/release-docs/licenses/LICENSE-javax.el.txt rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-jsoup.txt (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-logback.txt (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-mapstruct.txt (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-slf4j.txt (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-spring-plugin.txt (100%) rename seatunnel-web-dist/release-docs/{binary => }/licenses/LICENSE-swagger-core.txt (100%) create mode 100644 seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml rename seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml => seatunnel-web-dist/src/main/assembly/seatunnel-web.xml (59%) diff --git a/README.md b/README.md index 822472d82..4800fcff7 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ cd incubator-seatunnel-web sh build.sh code ``` -Then you can find the installer package in dir `incubator-seatunnel-web/seatunnel-server/seatunnel-app/target/apache-seatunnel-web-incubating-${project.version}.tar.gz`. +Then you can find the installer package in dir `incubator-seatunnel-web/seatunnel-web-dist/target/apache-seatunnel-web-incubating-${project.version}.tar.gz`. #### 3.2 Install diff --git a/build.sh b/build.sh index 6ce3ed7f9..9a8497036 100644 --- a/build.sh +++ b/build.sh @@ -29,7 +29,7 @@ DOCKER_VERSION=1.0.0-snapshot code() { /bin/sh $WORKDIR/mvnw clean package -DskipTests # mv release zip - mv $WORKDIR/seatunnel-server/seatunnel-app/target/apache-seatunnel-web-incubating-1.0.0-SNAPSHOT.zip $WORKDIR/ + mv $WORKDIR/seatunnel-web-dist/target/apache-seatunnel-web-incubating-1.0.0-SNAPSHOT.zip $WORKDIR/ } # build image diff --git a/pom.xml b/pom.xml index 6097e7c8e..b2a4da102 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,8 @@ seatunnel-server + seatunnel-datasource + seatunnel-web-dist @@ -97,6 +99,7 @@ 1.9.5 3.1.0 true + 3.3.0 1.18.0 false @@ -108,12 +111,20 @@ 2.1.214 5.9.0 4.4 - 3.4 + 3.12.0 19.0 3.10.0 2.17.1 4.2.0 2.3.1 + 2.1.0.9 + 3.1.4 + 1.11.271 + 1.0.1 + 1.18.24 + true + 3.1.1 + 1.3.0 @@ -237,6 +248,13 @@ ${seatunnel-framework.version} + + org.projectlombok + lombok + ${lombok.version} + provided + + org.apache.seatunnel @@ -402,6 +420,53 @@ ${maven-assembly-plugin.version} + + + org.apache.maven.plugins + maven-shade-plugin + ${maven-shade-plugin.version} + + false + true + + true + + + org.slf4j:* + ch.qos.logback:* + log4j:* + org.apache.logging.log4j:* + commons-logging:* + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + shade + + package + + + + + + + + + + org.apache.maven.plugins @@ -494,7 +559,7 @@ license-maven-plugin ${maven-license-maven-plugin} - ${project.basedir}/seatunnel-dist/target/ + ${project.basedir}/seatunnel-web-dist/target/ THIRD-PARTY.txt false false @@ -505,6 +570,37 @@ test,provided + + + org.codehaus.mojo + flatten-maven-plugin + ${flatten-maven-plugin.version} + + true + resolveCiFriendliesOnly + + + + flatten + + flatten + + process-resources + + + flatten.clean + + clean + + clean + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + diff --git a/seatunnel-datasource/pom.xml b/seatunnel-datasource/pom.xml new file mode 100644 index 000000000..a6fcbc7a5 --- /dev/null +++ b/seatunnel-datasource/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-web + ${revision} + + + seatunnel-datasource + pom + + + seatunnel-datasource-client + seatunnel-datasource-plugins + + + + + com.google.auto.service + auto-service + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-client/pom.xml b/seatunnel-datasource/seatunnel-datasource-client/pom.xml new file mode 100644 index 000000000..e697c6777 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource + ${revision} + + + seatunnel-datasource-client + + + + org.apache.seatunnel + seatunnel-api + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + + org.apache.seatunnel + datasource-all + ${project.version} + provided + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + ${e2e.dependency.skip} + true + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java new file mode 100644 index 000000000..7cc2783ed --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.exception.DataSourceSDKException; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.service.DataSourceService; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class AbstractDataSourceClient implements DataSourceService { + + private Map supportedDataSourceInfo = new HashMap<>(); + + private Map supportedDataSourceIndex = new HashMap<>(); + + protected List supportedDataSources = new ArrayList<>(); + + private List dataSourceChannels = new ArrayList<>(); + + protected AbstractDataSourceClient() { + AtomicInteger dataSourceIndex = new AtomicInteger(); + ServiceLoader.load(DataSourceFactory.class) + .forEach( + seaTunnelDataSourceFactory -> { + seaTunnelDataSourceFactory + .supportedDataSources() + .forEach( + dataSourceInfo -> { + supportedDataSourceInfo.put( + dataSourceInfo.getName().toUpperCase(), + dataSourceInfo); + supportedDataSourceIndex.put( + dataSourceInfo.getName().toUpperCase(), + dataSourceIndex.get()); + supportedDataSources.add(dataSourceInfo); + }); + dataSourceChannels.add(seaTunnelDataSourceFactory.createChannel()); + dataSourceIndex.getAndIncrement(); + }); + if (supportedDataSourceInfo.isEmpty()) { + throw new DataSourceSDKException("No supported data source found"); + } + } + + @Override + public Boolean checkDataSourceConnectivity( + String pluginName, Map dataSourceParams) { + return getDataSourceChannel(pluginName) + .checkDataSourceConnectivity(pluginName, dataSourceParams); + } + + @Override + public List listAllDataSources() { + return supportedDataSources; + } + + protected DataSourceChannel getDataSourceChannel(String pluginName) { + checkNotNull(pluginName, "pluginName cannot be null"); + Integer index = supportedDataSourceIndex.get(pluginName.toUpperCase()); + if (index == null) { + throw new DataSourceSDKException( + "The %s plugin is not supported or plugin not exist.", pluginName); + } + return dataSourceChannels.get(index); + } + + @Override + public OptionRule queryDataSourceFieldByName(String pluginName) { + return getDataSourceChannel(pluginName).getDataSourceOptions(pluginName); + } + + @Override + public OptionRule queryMetadataFieldByName(String pluginName) { + return getDataSourceChannel(pluginName) + .getDatasourceMetadataFieldsByDataSourceName(pluginName); + } + + @Override + public List getTables( + String pluginName, String databaseName, Map requestParams) { + return getDataSourceChannel(pluginName).getTables(pluginName, requestParams, databaseName); + } + + @Override + public List getDatabases(String pluginName, Map requestParams) { + return getDataSourceChannel(pluginName).getDatabases(pluginName, requestParams); + } + + @Override + public List getTableFields( + String pluginName, + Map requestParams, + String databaseName, + String tableName) { + return getDataSourceChannel(pluginName) + .getTableFields(pluginName, requestParams, databaseName, tableName); + } + + @Override + public Map> getTableFields( + String pluginName, + Map requestParams, + String databaseName, + List tableNames) { + return getDataSourceChannel(pluginName) + .getTableFields(pluginName, requestParams, databaseName, tableNames); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/DataSourceClient.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/DataSourceClient.java new file mode 100644 index 000000000..4bf46baa1 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/DataSourceClient.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource; + +import org.apache.seatunnel.datasource.annotation.ThreadSafe; + +@ThreadSafe +public class DataSourceClient extends AbstractDataSourceClient { + + public DataSourceClient() { + super(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java new file mode 100644 index 000000000..55794b81c --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface ThreadSafe {} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/exception/DataSourceSDKException.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/exception/DataSourceSDKException.java new file mode 100644 index 000000000..3f90eabc1 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/exception/DataSourceSDKException.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.exception; + +public class DataSourceSDKException extends RuntimeException { + + public DataSourceSDKException(String message) { + super(message); + } + + public DataSourceSDKException(String message, Throwable cause) { + super(message, cause); + } + + public DataSourceSDKException(Throwable cause) { + super(cause); + } + + public DataSourceSDKException(String message, Object... args) { + super(String.format(message, args)); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/PageQueryRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/PageQueryRequest.java new file mode 100644 index 000000000..8b09bc355 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/PageQueryRequest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.request; + +import lombok.Data; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +@Data +@SuperBuilder +public class PageQueryRequest { + + private Integer pageNum; + + private Integer pageSize; + + private String orderBy; + + private String order; + + private String search; + + Map fuzzySearchParamMap; + + Map exactSearchParamMap; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableCreateRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableCreateRequest.java new file mode 100644 index 000000000..3d7c42208 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableCreateRequest.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.request; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class VirtualTableCreateRequest { + + private Long id; + + private Long dataSourceId; + + private String description; + + private String databaseName; + + private String tableName; + + private List fields; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableFieldRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableFieldRequest.java new file mode 100644 index 000000000..f2acb83c2 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableFieldRequest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.request; + +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +@Data +@Builder +public class VirtualTableFieldRequest { + + private String type; + + private String name; + + private String comment; + + private Boolean primaryKey; + + private String defaultValue; + + private Boolean nullable; + + private Map properties; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableUpdateRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableUpdateRequest.java new file mode 100644 index 000000000..0939687c6 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableUpdateRequest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.request; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class VirtualTableUpdateRequest { + + private String description; + + private String databaseName; + + private String tableName; + + private List fields; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/BaseResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/BaseResponse.java new file mode 100644 index 000000000..831a2a64a --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/BaseResponse.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.response; + +import lombok.Data; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +@Data +@SuperBuilder +public class BaseResponse { + + /** create time */ + private Date createTime; + + /** update time */ + private Date updateTime; + + private String createUserId; + + private String updateUserId; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/DataSourceDetailResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/DataSourceDetailResponse.java new file mode 100644 index 000000000..f489ad455 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/DataSourceDetailResponse.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.response; + +import lombok.Data; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +@Data +@SuperBuilder +public class DataSourceDetailResponse extends BaseResponse { + + private Long id; + + private String dataSourceName; + + private String pluginName; + + private String comment; + + private Map params; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageDataResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageDataResponse.java new file mode 100644 index 000000000..24e801cce --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageDataResponse.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.response; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class PageDataResponse { + private Integer pageNum; + private Integer pageSize; + private Integer total; + private List datas; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageResultResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageResultResponse.java new file mode 100644 index 000000000..10bab20c8 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageResultResponse.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.response; + +import lombok.Data; + +import java.util.List; + +@Data +public class PageResultResponse { + + private Integer pageNum; + + private Integer pageSize; + + private Integer total; + + private Integer pages; + + private List dataList; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableDetailResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableDetailResponse.java new file mode 100644 index 000000000..63ab5d575 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableDetailResponse.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.response; + +import lombok.Data; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +@Data +@SuperBuilder +public class VirtualTableDetailResponse extends BaseResponse { + + private Long id; + + private Long dataSourceId; + + private String description; + + private String type; + + private String databaseName; + + private String tableName; + + private List fields; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableFieldResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableFieldResponse.java new file mode 100644 index 000000000..5e8583ef4 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableFieldResponse.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.response; + +import lombok.Data; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +@Data +@SuperBuilder +public class VirtualTableFieldResponse { + + private String type; + + private String name; + + private String comment; + + private Boolean primaryKey; + + private String defaultValue; + + private Boolean nullable; + + private Map properties; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableResponse.java new file mode 100644 index 000000000..8e061ad49 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableResponse.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.response; + +import lombok.Data; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +public class VirtualTableResponse extends BaseResponse { + + private Long id; + + private Long dataSourceId; + + private String description; + + private String databaseName; + + private String tableName; +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java new file mode 100644 index 000000000..9ab18325d --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.service; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import java.util.List; +import java.util.Map; + +public interface DataSourceService { + + /** + * get all data source plugins + * + * @return data source plugins info + */ + List listAllDataSources(); + + /** + * get data source plugin fields + * + * @param pluginName data source name + * @return data source plugin fields + */ + OptionRule queryDataSourceFieldByName(String pluginName); + + /** + * get data source metadata fields + * + * @param pluginName data source name + * @return data source metadata fields + */ + OptionRule queryMetadataFieldByName(String pluginName); + + /** + * check data source params is valid and connectable + * + * @param parameters data source params eg mysql plugin key: url // jdbc url key: username key: + * password other key... + * @return true if valid, false if invalid + */ + /** + * we can use this method to check data source connectivity + * + * @param dataSourceParams data source params + * @return check result + */ + Boolean checkDataSourceConnectivity(String pluginName, Map datasourceParams); + + /** + * get data source table names by database name + * + * @param pluginName plugin name + * @param databaseName database name + * @param requestParams connection params + * @return table names + */ + List getTables( + String pluginName, String databaseName, Map requestParams); + + /** + * get data source database names + * + * @param pluginName plugin name + * @param requestParams connection params + * @return database names + */ + List getDatabases(String pluginName, Map requestParams); + + /** + * get data source table fields + * + * @param pluginName plugin name + * @param requestParams connection params + * @param databaseName database name + * @param tableName table name + * @return table fields + */ + List getTableFields( + String pluginName, + Map requestParams, + String databaseName, + String tableName); + + /** + * get data source table fields + * + * @param pluginName plugin name + * @param requestParams connection params + * @param databaseName database name + * @param tableNames table names + * @return table fields + */ + Map> getTableFields( + String pluginName, + Map requestParams, + String databaseName, + List tableNames); +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java new file mode 100644 index 000000000..ece18206a --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.whaleops.datasource.s3; + +import org.apache.seatunnel.datasource.plugin.s3.S3DatasourceChannel; + +import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +@Disabled +class S3DatasourceChannelTest { + private static S3DatasourceChannel S3_DATASOURCE_CHANNEL = new S3DatasourceChannel(); + + @Test + void checkDataSourceConnectivity() { + Assertions.assertDoesNotThrow( + () -> { + S3_DATASOURCE_CHANNEL.checkDataSourceConnectivity("S3", createRequestParams()); + }); + } + + private Map createRequestParams() { + Map requestParams = + new ImmutableMap.Builder() + .put("bucket", "s3a://poc-kuke") + .put("fs.s3a.endpoint", "s3.cn-north-1.amazonaws.com.cn") + .put( + "fs.s3a.aws.credentials.provider", + "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider") + .put("access_key", "AKIAYYUV5DMXADXRBGTA") + .put("secret_key", "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") + .put("hadoop_s3_properties", "") + .build(); + return requestParams; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java new file mode 100644 index 000000000..e2877e88b --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class DataSourceClientTest { + private static final DataSourceClient DATA_SOURCE_CLIENT = new DataSourceClient(); + + @Test + public void listAllDataSources() { + Assertions.assertTrue( + DATA_SOURCE_CLIENT.listAllDataSources().stream() + .anyMatch( + dataSourcePluginInfo -> + StringUtils.equalsAnyIgnoreCase( + dataSourcePluginInfo.getName(), "jdbc-mysql"))); + Assertions.assertTrue( + DATA_SOURCE_CLIENT.listAllDataSources().stream() + .anyMatch( + dataSourcePluginInfo -> + StringUtils.equalsAnyIgnoreCase( + dataSourcePluginInfo.getName(), "kafka"))); + Assertions.assertTrue( + DATA_SOURCE_CLIENT.listAllDataSources().stream() + .anyMatch( + dataSourcePluginInfo -> + StringUtils.equalsAnyIgnoreCase( + dataSourcePluginInfo.getName(), "elasticsearch"))); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-all/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-all/pom.xml new file mode 100644 index 000000000..64d904df9 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-all/pom.xml @@ -0,0 +1,112 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-all + + + + org.apache.seatunnel + datasource-jdbc-clickhouse + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-hive + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-mysql + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-oracle + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-postgresql + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-tidb + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-redshift + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-sqlserver + ${project.version} + + + org.apache.seatunnel + datasource-jdbc-starrocks + ${project.version} + + + + org.apache.seatunnel + datasource-kafka + ${project.version} + + + + org.apache.seatunnel + datasource-elasticsearch + ${project.version} + + + org.apache.seatunnel + datasource-s3-redshift + ${project.version} + + + org.apache.seatunnel + datasource-starrocks + ${project.version} + + + org.apache.seatunnel + datasource-mysql-cdc + ${project.version} + + + org.apache.seatunnel + datasource-s3 + ${project.version} + + + org.apache.seatunnel + datasource-sqlserver-cdc + ${project.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/pom.xml new file mode 100644 index 000000000..7d2272b78 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-elasticsearch + + + 7.5.1 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + org.elasticsearch.client + elasticsearch-rest-client + ${elasticsearch-rest-client.version} + + + io.airlift + security + 206 + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java new file mode 100644 index 000000000..17495487f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.elasticsearch.client.EsRestClient; + +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; + +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ElasticSearchDataSourceChannel implements DataSourceChannel { + + private static final String DATABASE = "default"; + + @Override + public boolean canAbleGetSchema() { + return true; + } + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return ElasticSearchOptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return ElasticSearchOptionRule.metadataRule(); + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + databaseCheck(database); + try (EsRestClient client = + EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { + return client.listIndex(); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + return DEFAULT_DATABASES; + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (EsRestClient client = + EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { + client.getClusterInfo(); + return true; + } catch (Throwable e) { + throw new DataSourcePluginException( + "check ElasticSearch connectivity failed, " + e.getMessage(), e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + databaseCheck(database); + try (EsRestClient client = + EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { + Map fieldTypeMapping = client.getFieldTypeMapping(table); + List fields = new ArrayList<>(); + fieldTypeMapping.forEach( + (fieldName, fieldType) -> + fields.add(convertToTableField(fieldName, fieldType))); + return fields; + } catch (Exception ex) { + throw new DataSourcePluginException("Get table fields failed", ex); + } + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + databaseCheck(database); + Map> tableFields = new HashMap<>(); + tables.forEach( + table -> + tableFields.put( + table, getTableFields(pluginName, requestParams, database, table))); + return tableFields; + } + + private static void databaseCheck(@NonNull String database) { + if (!StringUtils.equalsAnyIgnoreCase(database, DATABASE)) { + throw new IllegalArgumentException("database not found: " + database); + } + } + + private TableField convertToTableField(String fieldName, String fieldType) { + TableField tableField = new TableField(); + tableField.setName(fieldName); + tableField.setType(fieldType); + tableField.setComment(null); + tableField.setNullable(true); + tableField.setPrimaryKey(fieldName.equals("_id")); + tableField.setDefaultValue(null); + return tableField; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceConfig.java new file mode 100644 index 000000000..97eec189f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceConfig.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch; + +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +public class ElasticSearchDataSourceConfig { + + public static final String PLUGIN_NAME = "ElasticSearch"; + + public static final String PLUGIN_VERSION = "1.0.0"; + + public static final DataSourcePluginInfo ELASTICSEARCH_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version(PLUGIN_VERSION) + .type(DatasourcePluginTypeEnum.NO_STRUCTURED.getCode()) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactory.java new file mode 100644 index 000000000..f9d01cae3 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactory.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; + +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class ElasticSearchDataSourceFactory implements DataSourceFactory { + + public static final String PLUGIN_NAME = "ElasticSearch"; + + @Override + public String factoryIdentifier() { + return PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(ElasticSearchDataSourceConfig.ELASTICSEARCH_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new ElasticSearchDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchOptionRule.java new file mode 100644 index 000000000..510978797 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchOptionRule.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +import java.util.List; + +public class ElasticSearchOptionRule { + + public static final Option> HOSTS = + Options.key("hosts") + .listType() + .noDefaultValue() + .withDescription( + "Elasticsearch cluster http address, the format is host:port, allowing multiple hosts to be specified. Such as [\"host1:9200\", \"host2:9200\"]"); + + public static final Option INDEX = + Options.key("index") + .stringType() + .noDefaultValue() + .withDescription("Elasticsearch index name, support * fuzzy matching"); + + public static final Option USERNAME = + Options.key("username") + .stringType() + .noDefaultValue() + .withDescription("x-pack username"); + + public static final Option PASSWORD = + Options.key("password") + .stringType() + .noDefaultValue() + .withDescription("x-pack password"); + + public static final Option TLS_VERIFY_CERTIFICATE = + Options.key("tls_verify_certificate") + .booleanType() + .defaultValue(true) + .withDescription("Enable certificates validation for HTTPS endpoints"); + + public static final Option TLS_VERIFY_HOSTNAME = + Options.key("tls_verify_hostname") + .booleanType() + .defaultValue(true) + .withDescription("Enable hostname validation for HTTPS endpoints"); + + public static final Option TLS_KEY_STORE_PATH = + Options.key("tls_keystore_path") + .stringType() + .noDefaultValue() + .withDescription( + "The path to the PEM or JKS key store. This file must be readable by the operating system user running SeaTunnel."); + + public static final Option TLS_KEY_STORE_PASSWORD = + Options.key("tls_keystore_password") + .stringType() + .noDefaultValue() + .withDescription("The key password for the key store specified"); + + public static final Option TLS_TRUST_STORE_PATH = + Options.key("tls_truststore_path") + .stringType() + .noDefaultValue() + .withDescription( + "The path to PEM or JKS trust store. This file must be readable by the operating system user running SeaTunnel."); + + public static final Option TLS_TRUST_STORE_PASSWORD = + Options.key("tls_truststore_password") + .stringType() + .noDefaultValue() + .withDescription("The key password for the trust store specified"); + + public static OptionRule optionRule() { + return OptionRule.builder() + .required(HOSTS) + .optional( + USERNAME, + PASSWORD, + TLS_VERIFY_CERTIFICATE, + TLS_VERIFY_HOSTNAME, + TLS_KEY_STORE_PATH, + TLS_KEY_STORE_PASSWORD, + TLS_TRUST_STORE_PATH, + TLS_TRUST_STORE_PASSWORD) + .build(); + } + + public static OptionRule metadataRule() { + return OptionRule.builder().required(INDEX).build(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ElasticsearchClusterInfo.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ElasticsearchClusterInfo.java new file mode 100644 index 000000000..f02516ce9 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ElasticsearchClusterInfo.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch.client; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@Builder +@ToString +public class ElasticsearchClusterInfo { + private String distribution; + private String clusterVersion; +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java new file mode 100644 index 000000000..1ef2e7050 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java @@ -0,0 +1,371 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch.client; + +import org.apache.seatunnel.common.utils.JsonUtils; +import org.apache.seatunnel.datasource.plugin.elasticsearch.ElasticSearchOptionRule; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.JsonNode; +import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.seatunnel.shade.com.typesafe.config.Config; + +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpHost; +import org.apache.http.HttpStatus; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; + +import javax.net.ssl.SSLContext; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Slf4j +public class EsRestClient implements AutoCloseable { + + private static final int CONNECTION_REQUEST_TIMEOUT = 10 * 1000; + + private static final int SOCKET_TIMEOUT = 5 * 60 * 1000; + + private final RestClient restClient; + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private EsRestClient(RestClient restClient) { + this.restClient = restClient; + } + + public static EsRestClient createInstance(Config pluginConfig) { + try { + List hosts = + OBJECT_MAPPER.readValue( + pluginConfig.getString(ElasticSearchOptionRule.HOSTS.key()), + List.class); + Optional username = Optional.empty(); + Optional password = Optional.empty(); + if (pluginConfig.hasPath(ElasticSearchOptionRule.USERNAME.key())) { + username = + Optional.of(pluginConfig.getString(ElasticSearchOptionRule.USERNAME.key())); + if (pluginConfig.hasPath(ElasticSearchOptionRule.PASSWORD.key())) { + password = + Optional.of( + pluginConfig.getString(ElasticSearchOptionRule.PASSWORD.key())); + } + } + Optional keystorePath = Optional.empty(); + Optional keystorePassword = Optional.empty(); + Optional truststorePath = Optional.empty(); + Optional truststorePassword = Optional.empty(); + boolean tlsVerifyCertificate = + ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.defaultValue(); + if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.key())) { + tlsVerifyCertificate = + pluginConfig.getBoolean( + ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.key()); + } + if (tlsVerifyCertificate) { + if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_KEY_STORE_PATH.key())) { + keystorePath = + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_KEY_STORE_PATH.key())); + } + if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_KEY_STORE_PASSWORD.key())) { + keystorePassword = + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_KEY_STORE_PASSWORD.key())); + } + if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_TRUST_STORE_PATH.key())) { + truststorePath = + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_TRUST_STORE_PATH.key())); + } + if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_TRUST_STORE_PASSWORD.key())) { + truststorePassword = + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_TRUST_STORE_PASSWORD + .key())); + } + } + boolean tlsVerifyHostnames = ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.defaultValue(); + if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.key())) { + tlsVerifyHostnames = + pluginConfig.getBoolean(ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.key()); + } + return createInstance( + hosts, + username, + password, + tlsVerifyCertificate, + tlsVerifyHostnames, + keystorePath, + keystorePassword, + truststorePath, + truststorePassword); + } catch (Exception e) { + throw new RuntimeException("Create EsRestClient failed", e); + } + } + + public static EsRestClient createInstance( + List hosts, + Optional username, + Optional password, + boolean tlsVerifyCertificate, + boolean tlsVerifyHostnames, + Optional keystorePath, + Optional keystorePassword, + Optional truststorePath, + Optional truststorePassword) { + RestClientBuilder restClientBuilder = + getRestClientBuilder( + hosts, + username, + password, + tlsVerifyCertificate, + tlsVerifyHostnames, + keystorePath, + keystorePassword, + truststorePath, + truststorePassword); + return new EsRestClient(restClientBuilder.build()); + } + + private static RestClientBuilder getRestClientBuilder( + List hosts, + Optional username, + Optional password, + boolean tlsVerifyCertificate, + boolean tlsVerifyHostnames, + Optional keystorePath, + Optional keystorePassword, + Optional truststorePath, + Optional truststorePassword) { + HttpHost[] httpHosts = new HttpHost[hosts.size()]; + for (int i = 0; i < hosts.size(); i++) { + httpHosts[i] = HttpHost.create(hosts.get(i)); + } + + RestClientBuilder restClientBuilder = + RestClient.builder(httpHosts) + .setRequestConfigCallback( + requestConfigBuilder -> + requestConfigBuilder + .setConnectionRequestTimeout( + CONNECTION_REQUEST_TIMEOUT) + .setSocketTimeout(SOCKET_TIMEOUT)); + + restClientBuilder.setHttpClientConfigCallback( + httpClientBuilder -> { + if (username.isPresent()) { + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(username.get(), password.get())); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + + try { + if (tlsVerifyCertificate) { + Optional sslContext = + SSLUtils.buildSSLContext( + keystorePath, + keystorePassword, + truststorePath, + truststorePassword); + sslContext.ifPresent(e -> httpClientBuilder.setSSLContext(e)); + } else { + SSLContext sslContext = + SSLContexts.custom() + .loadTrustMaterial(new TrustAllStrategy()) + .build(); + httpClientBuilder.setSSLContext(sslContext); + } + if (!tlsVerifyHostnames) { + httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return httpClientBuilder; + }); + return restClientBuilder; + } + + public ElasticsearchClusterInfo getClusterInfo() { + Request request = new Request("GET", "/"); + try { + Response response = restClient.performRequest(request); + String result = EntityUtils.toString(response.getEntity()); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(result); + JsonNode versionNode = jsonNode.get("version"); + return ElasticsearchClusterInfo.builder() + .clusterVersion(versionNode.get("number").asText()) + .distribution( + Optional.ofNullable(versionNode.get("distribution")) + .map(JsonNode::asText) + .orElse(null)) + .build(); + } catch (IOException e) { + throw new ResponseException("fail to get elasticsearch version.", e); + } + } + + public void close() { + try { + restClient.close(); + } catch (IOException e) { + log.warn("close elasticsearch connection error", e); + } + } + + public List listIndex() { + String endpoint = "/_cat/indices?format=json"; + Request request = new Request("GET", endpoint); + try { + Response response = restClient.performRequest(request); + if (response == null) { + throw new ResponseException("GET " + endpoint + " response null"); + } + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + String entity = EntityUtils.toString(response.getEntity()); + return JsonUtils.toList(entity, Map.class).stream() + .map(map -> map.get("index").toString()) + .collect(Collectors.toList()); + } else { + throw new ResponseException( + String.format( + "GET %s response status code=%d", + endpoint, response.getStatusLine().getStatusCode())); + } + } catch (IOException ex) { + throw new ResponseException(ex); + } + } + + public void dropIndex(String tableName) { + String endpoint = String.format("/%s", tableName); + Request request = new Request("DELETE", endpoint); + try { + Response response = restClient.performRequest(request); + if (response == null) { + throw new ResponseException("DELETE " + endpoint + " response null"); + } + // todo: if the index doesn't exist, the response status code is 200? + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new ResponseException( + String.format( + "DELETE %s response status code=%d", + endpoint, response.getStatusLine().getStatusCode())); + } + } catch (IOException ex) { + throw new ResponseException(ex); + } + } + + /** + * get es field name and type mapping relation + * + * @param index index name + * @return {key-> field name,value->es type} + */ + public Map getFieldTypeMapping(String index) { + String endpoint = String.format("/%s/_mappings", index); + Request request = new Request("GET", endpoint); + Map mapping = new HashMap<>(); + try { + Response response = restClient.performRequest(request); + if (response == null) { + throw new ResponseException("GET " + endpoint + " response null"); + } + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new ResponseException( + String.format( + "GET %s response status code=%d", + endpoint, response.getStatusLine().getStatusCode())); + } + String entity = EntityUtils.toString(response.getEntity()); + log.info(String.format("GET %s response=%s", endpoint, entity)); + ObjectNode responseJson = JsonUtils.parseObject(entity); + for (Iterator it = responseJson.elements(); it.hasNext(); ) { + JsonNode indexProperty = it.next(); + JsonNode mappingsProperty = indexProperty.get("mappings"); + if (mappingsProperty.has("mappingsProperty")) { + JsonNode properties = mappingsProperty.get("properties"); + mapping = getFieldTypeMappingFromProperties(properties); + } else { + for (JsonNode typeNode : mappingsProperty) { + JsonNode properties; + if (typeNode.has("properties")) { + properties = typeNode.get("properties"); + } else { + properties = typeNode; + } + mapping.putAll(getFieldTypeMappingFromProperties(properties)); + } + } + } + } catch (IOException ex) { + throw new ResponseException(ex); + } + return mapping; + } + + private static Map getFieldTypeMappingFromProperties(JsonNode properties) { + Map mapping = new HashMap<>(); + for (Iterator it = properties.fieldNames(); it.hasNext(); ) { + String field = it.next(); + JsonNode fieldProperty = properties.get(field); + if (fieldProperty == null) { + mapping.put(field, "text"); + } else { + if (fieldProperty.has("type")) { + String type = fieldProperty.get("type").asText(); + mapping.put(field, type); + } else { + log.warn( + String.format( + "fail to get elasticsearch field %s mapping type,so give a default type text", + field)); + mapping.put(field, "text"); + } + } + } + return mapping; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ResponseException.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ResponseException.java new file mode 100644 index 000000000..59417cb70 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ResponseException.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch.client; + +public class ResponseException extends RuntimeException { + + public ResponseException() { + super(); + } + + public ResponseException(String message) { + super(message); + } + + public ResponseException(String message, Throwable cause) { + super(message, cause); + } + + public ResponseException(Throwable cause) { + super(cause); + } + + protected ResponseException( + String message, + Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java new file mode 100644 index 000000000..9c10cd3b8 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch.client; + +import static java.util.Collections.list; + +import io.airlift.security.pem.PemReader; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import javax.security.auth.x500.X500Principal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +@SuppressWarnings("MagicNumber") +public final class SSLUtils { + + public static Optional buildSSLContext( + Optional keyStorePath, + Optional keyStorePassword, + Optional trustStorePath, + Optional trustStorePassword) + throws GeneralSecurityException, IOException { + if (!keyStorePath.isPresent() && !trustStorePath.isPresent()) { + return Optional.empty(); + } + return Optional.of( + createSSLContext( + keyStorePath, keyStorePassword, trustStorePath, trustStorePassword)); + } + + private static SSLContext createSSLContext( + Optional keyStorePath, + Optional keyStorePassword, + Optional trustStorePath, + Optional trustStorePassword) + throws GeneralSecurityException, IOException { + // load KeyStore if configured and get KeyManagers + KeyStore keyStore = null; + KeyManager[] keyManagers = null; + if (keyStorePath.isPresent()) { + File keyStoreFile = new File(keyStorePath.get()); + char[] keyManagerPassword; + try { + // attempt to read the key store as a PEM file + keyStore = PemReader.loadKeyStore(keyStoreFile, keyStoreFile, keyStorePassword); + // for PEM encoded keys, the password is used to decrypt the specific key (and does + // not protect the keystore itself) + keyManagerPassword = new char[0]; + } catch (IOException | GeneralSecurityException ignored) { + keyManagerPassword = keyStorePassword.map(String::toCharArray).orElse(null); + + keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try (InputStream in = new FileInputStream(keyStoreFile)) { + keyStore.load(in, keyManagerPassword); + } + } + validateCertificates(keyStore); + KeyManagerFactory keyManagerFactory = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, keyManagerPassword); + keyManagers = keyManagerFactory.getKeyManagers(); + } + + // load TrustStore if configured, otherwise use KeyStore + KeyStore trustStore = keyStore; + if (trustStorePath.isPresent()) { + File trustStoreFile = new File(trustStorePath.get()); + trustStore = loadTrustStore(trustStoreFile, trustStorePassword); + } + + // create TrustManagerFactory + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + + // get X509TrustManager + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new RuntimeException( + "Unexpected default trust managers:" + Arrays.toString(trustManagers)); + } + // create SSLContext + SSLContext result = SSLContext.getInstance("SSL"); + result.init(keyManagers, trustManagers, null); + return result; + } + + private static KeyStore loadTrustStore(File trustStorePath, Optional trustStorePassword) + throws IOException, GeneralSecurityException { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try { + // attempt to read the trust store as a PEM file + List certificateChain = PemReader.readCertificateChain(trustStorePath); + if (!certificateChain.isEmpty()) { + trustStore.load(null, null); + for (X509Certificate certificate : certificateChain) { + X500Principal principal = certificate.getSubjectX500Principal(); + trustStore.setCertificateEntry(principal.getName(), certificate); + } + return trustStore; + } + } catch (IOException | GeneralSecurityException ignored) { + // ignored + } + + try (InputStream in = new FileInputStream(trustStorePath)) { + trustStore.load(in, trustStorePassword.map(String::toCharArray).orElse(null)); + } + return trustStore; + } + + private static void validateCertificates(KeyStore keyStore) throws GeneralSecurityException { + for (String alias : list(keyStore.aliases())) { + if (!keyStore.isKeyEntry(alias)) { + continue; + } + Certificate certificate = keyStore.getCertificate(alias); + if (!(certificate instanceof X509Certificate)) { + continue; + } + + try { + ((X509Certificate) certificate).checkValidity(); + } catch (CertificateExpiredException e) { + throw new CertificateExpiredException( + "KeyStore certificate is expired: " + e.getMessage()); + } catch (CertificateNotYetValidException e) { + throw new CertificateNotYetValidException( + "KeyStore certificate is not yet valid: " + e.getMessage()); + } + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java new file mode 100644 index 000000000..b8b25d841 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch; + +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; + +// todo: use testcontainer to create container +@Disabled +class ElasticSearchDataSourceChannelTest { + private static final Logger LOGGER = + LoggerFactory.getLogger(ElasticSearchDataSourceChannelTest.class); + + private static final ElasticSearchDataSourceChannel ELASTIC_SEARCH_DATA_SOURCE_CHANNEL = + new ElasticSearchDataSourceChannel(); + + private static final String PLUGIN_NAME = "ElasticSearch"; + + private static final String DATABASE = "Default"; + + private static final Map REQUEST_MAP = + new ImmutableMap.Builder() + .put(ElasticSearchOptionRule.HOSTS.key(), "[\"http://localhost:9200\"]") + .build(); + + @Test + void canAbleGetSchema() { + Assertions.assertTrue(ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.canAbleGetSchema()); + } + + @Test + void getDataSourceOptions() { + Assertions.assertNotNull(ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDataSourceOptions(PLUGIN_NAME)); + } + + @Test + void getDatasourceMetadataFieldsByDataSourceName() { + Assertions.assertNotNull( + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName( + PLUGIN_NAME)); + } + + @Test + void getTables() { + Assertions.assertDoesNotThrow( + () -> { + List tables = + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTables( + PLUGIN_NAME, REQUEST_MAP, DATABASE); + LOGGER.info("{}", tables); + }); + } + + @Test + void getDatabases() { + Assertions.assertLinesMatch( + Lists.newArrayList("default"), + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatabases(PLUGIN_NAME, REQUEST_MAP)); + } + + @Test + void checkDataSourceConnectivity() { + Assertions.assertTrue( + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity( + PLUGIN_NAME, REQUEST_MAP)); + } + + @Test + void getTableFields() { + Assertions.assertDoesNotThrow( + () -> { + List tableFields = + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields( + PLUGIN_NAME, REQUEST_MAP, DATABASE, ""); + LOGGER.info("{}", tableFields); + }); + } + + @Test + void testGetTableFields() { + Assertions.assertDoesNotThrow( + () -> { + Map> tableFields = + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields( + PLUGIN_NAME, REQUEST_MAP, DATABASE, Lists.newArrayList("")); + LOGGER.info("{}", tableFields); + }); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java new file mode 100644 index 000000000..85a49e77b --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.elasticsearch; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class ElasticSearchDataSourceFactoryTest { + + private static final ElasticSearchDataSourceFactory ELASTIC_SEARCH_DATA_SOURCE_FACTORY = + new ElasticSearchDataSourceFactory(); + + @Test + void factoryIdentifier() { + Assertions.assertEquals( + "ElasticSearch", ELASTIC_SEARCH_DATA_SOURCE_FACTORY.factoryIdentifier()); + } + + @Test + void supportedDataSources() { + Assertions.assertFalse(ELASTIC_SEARCH_DATA_SOURCE_FACTORY.supportedDataSources().isEmpty()); + } + + @Test + void createChannel() { + Assertions.assertNotNull(ELASTIC_SEARCH_DATA_SOURCE_FACTORY.createChannel()); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/pom.xml new file mode 100644 index 000000000..1ca92dfb2 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-clickhouse + + + 0.3.2-patch11 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + + + com.clickhouse + clickhouse-jdbc + ${clickhouse.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java new file mode 100644 index 000000000..4862d41d8 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.clickhouse.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class ClickhouseDataSourceConfig { + public static final String PLUGIN_NAME = "JDBC-ClickHouse"; + + public static final DataSourcePluginInfo CLICKHOUSE_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .supportVirtualTables(false) + .build(); + + public static final Set CLICKHOUSE_SYSTEM_DATABASES = + Sets.newHashSet( + "system", + "default", + "information_schema", + "mysql", + "performance_schema", + "sys"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(ClickhouseOptionRule.URL, ClickhouseOptionRule.DRIVER) + .optional(ClickhouseOptionRule.USER, ClickhouseOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder() + .required(ClickhouseOptionRule.DATABASE, ClickhouseOptionRule.TABLE) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java new file mode 100644 index 000000000..371411741 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.clickhouse.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Slf4j +public class ClickhouseJdbcDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return ClickhouseDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return ClickhouseDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams);) { + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[]{"TABLE"}); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + Statement statement = connection.createStatement(); + ResultSet re = statement.executeQuery("SHOW DATABASES;")) { + // filter system databases + while (re.next()) { + String dbName = re.getString("name"); + if (StringUtils.isNotBlank(dbName) + && !ClickhouseDataSourceConfig.CLICKHOUSE_SYSTEM_DATABASES.contains( + dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (Exception ex) { + throw new RuntimeException("get databases failed", ex); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("Check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database)) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("Get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return null; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(ClickhouseOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(ClickhouseOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(ClickhouseOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(ClickhouseOptionRule.USER.key())) { + String username = requestParams.get(ClickhouseOptionRule.USER.key()); + String password = requestParams.get(ClickhouseOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceFactory.java new file mode 100644 index 000000000..f8b979864 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceFactory.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.clickhouse.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; + +import java.util.Set; + +@Slf4j +@AutoService(DataSourceFactory.class) +public class ClickhouseJdbcDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return ClickhouseDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(ClickhouseDataSourceConfig.CLICKHOUSE_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new ClickhouseJdbcDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java new file mode 100644 index 000000000..3beff6058 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.clickhouse.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class ClickhouseOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:clickhouse://localhost:8123/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .noDefaultValue() + .withDescription("driver"); + + public enum DriverType { + ClickHouse("ru.yandex.clickhouse.ClickHouseDriver"); + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/pom.xml new file mode 100644 index 000000000..c73b45020 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-hive + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcConstants.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcConstants.java new file mode 100644 index 000000000..8b133ac7d --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcConstants.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.hive.jdbc; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class HiveJdbcConstants { + + public static final Set HIVE_SYSTEM_DATABASES = + Sets.newHashSet( + "information_schema", "mysql", "performance_schema", "sys", "test", "hivedb"); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java new file mode 100644 index 000000000..32543c423 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.hive.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +public class HiveJdbcDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return HiveJdbcOptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return HiveJdbcOptionRule.metadataRule(); + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + return getTables(pluginName, requestParams, database); + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + try { + return getDataBaseNames(pluginName, requestParams); + } catch (SQLException e) { + log.error("Query Hive databases error, request params is {}", requestParams, e); + throw new DataSourcePluginException("Query Hive databases error,", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + return checkJdbcConnectivity(requestParams); + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + return getTableFields(requestParams, database, table); + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + Map> tableFields = new HashMap<>(tables.size()); + for (String table : tables) { + tableFields.put(table, getTableFields(requestParams, database, table)); + } + return tableFields; + } + + protected boolean checkJdbcConnectivity(Map requestParams) { + try (Connection ignored = init(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException( + "check jdbc connectivity failed, " + e.getMessage(), e); + } + } + + protected Connection init(Map requestParams) throws SQLException { + if (MapUtils.isEmpty(requestParams)) { + throw new DataSourcePluginException( + "Hive jdbc request params is null, please check your config"); + } + String url = requestParams.get(HiveJdbcOptionRule.URL.key()); + return DriverManager.getConnection(url); + } + + protected List getDataBaseNames(String pluginName, Map requestParams) + throws SQLException { + List dbNames = new ArrayList<>(); + try (Connection connection = init(requestParams); + Statement statement = connection.createStatement();) { + ResultSet re = statement.executeQuery("SHOW DATABASES;"); + // filter system databases + while (re.next()) { + String dbName = re.getString("database"); + if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(pluginName, dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } + } + + protected List getTableNames(Map requestParams, String dbName) { + List tableNames = new ArrayList<>(); + try (Connection connection = init(requestParams);) { + ResultSet resultSet = + connection.getMetaData().getTables(dbName, null, null, new String[]{"TABLE"}); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + protected List getTableFields( + Map requestParams, String dbName, String tableName) { + List tableFields = new ArrayList<>(); + try (Connection connection = init(requestParams);) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, dbName, tableName); + ResultSet resultSet = metaData.getColumns(dbName, null, tableName, null); + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + boolean isNullable = convertToBoolean(nullable); + tableField.setNullable(isNullable); + tableFields.add(tableField); + } + } catch (SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + @SuppressWarnings("checkstyle:MagicNumber") + private static boolean checkHostConnectable(String host, int port) { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(host, port), 1000); + return true; + } catch (IOException e) { + return false; + } + } + + private boolean isNotSystemDatabase(String pluginName, String dbName) { + // FIXME,filters system databases + return true; + } + + private boolean convertToBoolean(Object value) { + if (value instanceof Boolean) { + return (Boolean) value; + } + if (value instanceof String) { + return value.equals("TRUE"); + } + return false; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceFactory.java new file mode 100644 index 000000000..b149cc3b7 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceFactory.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.hive.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import java.util.HashSet; +import java.util.Set; + +public class HiveJdbcDataSourceFactory implements DataSourceFactory { + @Override + public String factoryIdentifier() { + return "Hive-JDBC"; + } + + @Override + public Set supportedDataSources() { + DataSourcePluginInfo dataSourcePluginInfo = + DataSourcePluginInfo.builder() + .name("Hive-JDBC") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .version("1.0.0") + .icon("Hive-JDBC") + .supportVirtualTables(false) + .build(); + Set dataSourceInfos = new HashSet<>(); + dataSourceInfos.add(dataSourcePluginInfo); + return dataSourceInfos; + } + + @Override + public DataSourceChannel createChannel() { + return new HiveJdbcDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcOptionRule.java new file mode 100644 index 000000000..fb004b69f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcOptionRule.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.hive.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +public class HiveJdbcOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:hive2://localhost:10000/default?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static OptionRule optionRule() { + return OptionRule.builder().required(URL).build(); + } + + public static OptionRule metadataRule() { + // todo + return OptionRule.builder().build(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/pom.xml new file mode 100644 index 000000000..11e0ea6b5 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-mysql + + + 8.0.28 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + + + mysql + mysql-connector-java + ${mysql-connector.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java new file mode 100644 index 000000000..2e2de3e45 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.mysql.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class MysqlDataSourceConfig { + + public static final String PLUGIN_NAME = "JDBC-Mysql"; + + public static final DataSourcePluginInfo MYSQL_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); + + public static final Set MYSQL_SYSTEM_DATABASES = + Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(MysqlOptionRule.URL, MysqlOptionRule.DRIVER) + .optional(MysqlOptionRule.USER, MysqlOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder().required(MysqlOptionRule.DATABASE, MysqlOptionRule.TABLE).build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java new file mode 100644 index 000000000..1fe420d53 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.mysql.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class MysqlJdbcDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return MysqlDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return MysqlDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[]{"TABLE"})) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { + // filter system databases + while (re.next()) { + String dbName = re.getString("database"); + if (StringUtils.isNotBlank(dbName) + && !MysqlDataSourceConfig.MYSQL_SYSTEM_DATABASES.contains(dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("Get databases failed", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database)) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return tables.parallelStream() + .collect( + Collectors.toMap( + Function.identity(), + table -> + getTableFields( + pluginName, requestParams, database, table))); + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(MysqlOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(MysqlOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(MysqlOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(MysqlOptionRule.USER.key())) { + String username = requestParams.get(MysqlOptionRule.USER.key()); + String password = requestParams.get(MysqlOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceFactory.java new file mode 100644 index 000000000..31f6439bf --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceFactory.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.mysql.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; + +import java.util.Set; + +@Slf4j +@AutoService(DataSourceFactory.class) +public class MysqlJdbcDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return MysqlDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(MysqlDataSourceConfig.MYSQL_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new MysqlJdbcDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java new file mode 100644 index 000000000..f666251fc --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.mysql.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class MysqlOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.MYSQL) + .withDescription("driver"); + + public enum DriverType { + MYSQL("com.mysql.cj.jdbc.Driver"), + ; + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/pom.xml new file mode 100644 index 000000000..1e48c9b81 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-oracle + + + 21.5.0.0 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + + + com.oracle.database.jdbc + ojdbc8 + ${oracle-jdbc.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java new file mode 100644 index 000000000..91ccc6615 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.oracle.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class OracleDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return OracleDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return OracleDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[]{"TABLE"});) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { + // filter system databases + while (re.next()) { + String dbName = re.getString("database"); + if (StringUtils.isNotBlank(dbName) + && !OracleDataSourceConfig.ORACLE_SYSTEM_DATABASES.contains(dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (Exception ex) { + throw new RuntimeException("get databases failed", ex); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database)) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return null; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(OracleOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(OracleOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(OracleOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(OracleOptionRule.USER.key())) { + String username = requestParams.get(OracleOptionRule.USER.key()); + String password = requestParams.get(OracleOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java new file mode 100644 index 000000000..96f09fbef --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.oracle.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class OracleDataSourceConfig { + + public static final String PLUGIN_NAME = "JDBC-Oracle"; + + public static final DataSourcePluginInfo ORACLE_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); + + public static final Set ORACLE_SYSTEM_DATABASES = + Sets.newHashSet("SYS", "SYSTEM", "SYSDBA", "SYSOPER", "HR", "SCOTT"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(OracleOptionRule.URL, OracleOptionRule.DRIVER) + .optional(OracleOptionRule.USER, OracleOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder() + .required(OracleOptionRule.DATABASE, OracleOptionRule.TABLE) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleJdbcDataSourceFactory.java new file mode 100644 index 000000000..b6a0aec9c --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleJdbcDataSourceFactory.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.oracle.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; + +import java.util.Set; + +@Slf4j +@AutoService(DataSourceFactory.class) +public class OracleJdbcDataSourceFactory implements DataSourceFactory { + @Override + public String factoryIdentifier() { + return OracleDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(OracleDataSourceConfig.ORACLE_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new OracleDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java new file mode 100644 index 000000000..517a8c32e --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.oracle.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class OracleOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription("jdbc url, eg:" + "jdbc:oracle:thin:@localhost:1521:XE"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.ORACLE) + .withDescription("driver"); + + public enum DriverType { + ORACLE("oracle.jdbc.driver.OracleDriver"), + ; + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/pom.xml new file mode 100644 index 000000000..cffbf2513 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-postgresql + + + 42.4.3 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + + + org.postgresql + postgresql + ${postgresql.version} + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java new file mode 100644 index 000000000..b331d61d0 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.postgresql.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class PostgresqlDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return PostgresqlDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return PostgresqlDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + String query = "SELECT table_schema, table_name FROM information_schema.tables"; + try (Connection connection = getConnection(requestParams, database)) { + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(query)) { + while (resultSet.next()) { + String schemaName = resultSet.getString("table_schema"); + String tableName = resultSet.getString("table_name"); + if (StringUtils.isNotBlank(schemaName) + && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains( + schemaName)) { + tableNames.add(schemaName + "." + tableName); + } + } + } + return tableNames; + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + PreparedStatement statement = + connection.prepareStatement("select datname from pg_database;"); + ResultSet re = statement.executeQuery()) { + while (re.next()) { + String dbName = re.getString("datname"); + if (StringUtils.isNotBlank(dbName) + && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains( + dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("get databases failed", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database);) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + String[] split = table.split("\\."); + if (split.length != 2) { + throw new DataSourcePluginException( + "Postgresql tableName should composed by schemaName.tableName"); + } + try (ResultSet resultSet = metaData.getColumns(database, split[0], split[1], null)) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return null; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(PostgresqlOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(PostgresqlOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(PostgresqlOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(PostgresqlOptionRule.USER.key())) { + String username = requestParams.get(PostgresqlOptionRule.USER.key()); + String password = requestParams.get(PostgresqlOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java new file mode 100644 index 000000000..2aa0538b2 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.postgresql.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class PostgresqlDataSourceConfig { + + public static final String PLUGIN_NAME = "JDBC-Postgres"; + + public static final DataSourcePluginInfo POSTGRESQL_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); + + public static final Set POSTGRESQL_SYSTEM_DATABASES = + Sets.newHashSet( + "information_schema", + "pg_catalog", + "root", + "pg_toast", + "pg_temp_1", + "pg_toast_temp_1", + "postgres", + "template0", + "template1"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(PostgresqlOptionRule.URL, PostgresqlOptionRule.DRIVER) + .optional(PostgresqlOptionRule.USER, PostgresqlOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder() + .required(PostgresqlOptionRule.DATABASE, PostgresqlOptionRule.TABLE) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceFactory.java new file mode 100644 index 000000000..13dd7847c --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceFactory.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.postgresql.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; + +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class PostgresqlDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return PostgresqlDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(PostgresqlDataSourceConfig.POSTGRESQL_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new PostgresqlDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java new file mode 100644 index 000000000..16e8b85b2 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.postgresql.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class PostgresqlOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:postgresql://localhost:5432//test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.POSTGRESQL) + .withDescription("driver"); + + public enum DriverType { + POSTGRESQL("org.postgresql.Driver"), + ; + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/pom.xml new file mode 100644 index 000000000..fc3691277 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-redshift + + + 2.1.0.10 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + + + com.amazon.redshift + redshift-jdbc42 + ${redshift.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java new file mode 100644 index 000000000..32f77e985 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class RedshiftDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return RedshiftDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return RedshiftDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database); + ResultSet resultSet = + connection.getMetaData().getTables(database, null, null, null);) { + while (resultSet.next()) { + String schemaName = resultSet.getString("TABLE_SCHEM"); + String tableName = resultSet.getString("TABLE_NAME"); + // todo: use isNotSystemSchemaName + if (StringUtils.isNotBlank(schemaName) + && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(schemaName)) { + tableNames.add(schemaName + "." + tableName); + } + } + return tableNames; + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + PreparedStatement statement = + connection.prepareStatement("select datname from pg_database;"); + ResultSet re = statement.executeQuery()) { + while (re.next()) { + String dbName = re.getString("datname"); + if (StringUtils.isNotBlank(dbName) + && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("get databases failed", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database);) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + String[] split = table.split("\\."); + if (split.length != 2) { + throw new DataSourcePluginException( + "Postgresql tableName should composed by schemaName.tableName"); + } + try (ResultSet resultSet = metaData.getColumns(database, split[0], split[1], null)) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return null; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(RedshiftOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(RedshiftOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(RedshiftOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(RedshiftOptionRule.USER.key())) { + String username = requestParams.get(RedshiftOptionRule.USER.key()); + String password = requestParams.get(RedshiftOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java new file mode 100644 index 000000000..450c8d954 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class RedshiftDataSourceConfig { + + public static final String PLUGIN_NAME = "JDBC-Redshift"; + + public static final DataSourcePluginInfo REDSHIFT_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon("redshift") + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); + + public static final Set REDSHIFT_SYSTEM_TABLES = + Sets.newHashSet( + "information_schema", + "pg_catalog", + "root", + "pg_toast", + "pg_temp_1", + "pg_toast_temp_1", + "postgres", + "template0", + "template1"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(RedshiftOptionRule.URL, RedshiftOptionRule.DRIVER) + .optional(RedshiftOptionRule.USER, RedshiftOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder() + .required(RedshiftOptionRule.DATABASE, RedshiftOptionRule.TABLE) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceFactory.java new file mode 100644 index 000000000..bf33a24c3 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceFactory.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; + +import java.util.Set; + +@Slf4j +@AutoService(DataSourceFactory.class) +public class RedshiftDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return RedshiftDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(RedshiftDataSourceConfig.REDSHIFT_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new RedshiftDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java new file mode 100644 index 000000000..ba4c11c0f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class RedshiftOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:redshift://server.redshift.amazonaws.com:5439/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.JDBC42_REDSHIFT) + .withDescription("driver"); + + public enum DriverType { + JDBC42_REDSHIFT("com.amazon.redshift.jdbc42.Driver"), + ; + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/pom.xml new file mode 100644 index 000000000..5452a7e8d --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-sqlserver + + + 9.2.1.jre8 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + + + com.microsoft.sqlserver + mssql-jdbc + ${sqlserver.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java new file mode 100644 index 000000000..bde7550c9 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.sqlserver.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Slf4j +public class SqlServerDataSourceChannel implements DataSourceChannel { + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return SqlServerDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return SqlServerDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[]{"TABLE"})) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { + // filter system databases + while (re.next()) { + String dbName = re.getString("database"); + if (StringUtils.isNotBlank(dbName) + && !SqlServerDataSourceConfig.SQLSERVER_SYSTEM_DATABASES.contains(dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (Exception ex) { + throw new RuntimeException("get databases failed", ex); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database)) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return null; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(SqlServerOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(SqlServerOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(SqlServerOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(SqlServerOptionRule.USER.key())) { + String username = requestParams.get(SqlServerOptionRule.USER.key()); + String password = requestParams.get(SqlServerOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java new file mode 100644 index 000000000..3bfc056c1 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.sqlserver.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class SqlServerDataSourceConfig { + + public static final String PLUGIN_NAME = "JDBC-SQLServer"; + + public static final DataSourcePluginInfo SQLSERVER_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); + + public static final Set SQLSERVER_SYSTEM_DATABASES = + Sets.newHashSet( + "master", + "tempdb", + "model", + "msdb", + "ReportServer", + "ReportServerTempDB", + "SSISDB"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(SqlServerOptionRule.URL, SqlServerOptionRule.DRIVER) + .optional(SqlServerOptionRule.USER, SqlServerOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder() + .required(SqlServerOptionRule.DATABASE, SqlServerOptionRule.TABLE) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceFactory.java new file mode 100644 index 000000000..b9a21ce49 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceFactory.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.sqlserver.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; + +import java.util.Set; + +@Slf4j +@AutoService(DataSourceFactory.class) +public class SqlServerDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return SqlServerDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(SqlServerDataSourceConfig.SQLSERVER_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new SqlServerDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java new file mode 100644 index 000000000..ec157d39c --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.sqlserver.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class SqlServerOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + "jdbc:sqlserver://localhost:1433;database=xx"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.SQL_SERVER) + .withDescription("driver"); + + public enum DriverType { + SQL_SERVER("com.microsoft.sqlserver.jdbc.SQLServerDriver"), + ; + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/pom.xml new file mode 100644 index 000000000..76a5885fa --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-starrocks + + + 8.0.28 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + + + mysql + mysql-connector-java + ${mysql-connector.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java new file mode 100644 index 000000000..427657ccc --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class StarRocksDataSourceConfig { + + public static final String PLUGIN_NAME = "JDBC-StarRocks"; + + public static final DataSourcePluginInfo STAR_ROCKS_DATA_SOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); + + public static final Set STAR_ROCKS_SYSTEM_DATABASES = + Sets.newHashSet("_statistics_", "information_schema"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(StarRocksOptionRule.URL, StarRocksOptionRule.DRIVER) + .optional(StarRocksOptionRule.USER, StarRocksOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder() + .required(StarRocksOptionRule.DATABASE, StarRocksOptionRule.TABLE) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java new file mode 100644 index 000000000..3f429a7cc --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class StarRocksJdbcDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return StarRocksDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return StarRocksDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[]{"TABLE"})) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { + // filter system databases + while (re.next()) { + String dbName = re.getString("database"); + if (StringUtils.isNotBlank(dbName) + && !StarRocksDataSourceConfig.STAR_ROCKS_SYSTEM_DATABASES.contains( + dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (Exception ex) { + throw new DataSourcePluginException("get databases failed", ex); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database)) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + try (ResultSet resultSet = metaData.getColumns(database, null, table, null);) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return null; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(StarRocksOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(StarRocksOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(StarRocksOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(StarRocksOptionRule.USER.key())) { + String username = requestParams.get(StarRocksOptionRule.USER.key()); + String password = requestParams.get(StarRocksOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceFactory.java new file mode 100644 index 000000000..65620d0ec --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceFactory.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; + +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class StarRocksJdbcDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return StarRocksDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(StarRocksDataSourceConfig.STAR_ROCKS_DATA_SOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new StarRocksJdbcDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java new file mode 100644 index 000000000..d67c2a304 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class StarRocksOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:mysql://localhost:9030/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.MYSQL) + .withDescription("driver"); + + public enum DriverType { + MYSQL("com.mysql.cj.jdbc.Driver"), + ; + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/pom.xml new file mode 100644 index 000000000..5797396d4 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/pom.xml @@ -0,0 +1,36 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-jdbc-tidb + + + + + + org.apache.seatunnel + datasource-jdbc-mysql + ${project.version} + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java new file mode 100644 index 000000000..f02caff2f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.tidb.jdbc; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.common.collect.Sets; + +import java.util.Set; + +public class TidbDataSourceConfig { + + public static final String PLUGIN_NAME = "JDBC-TiDB"; + + public static final DataSourcePluginInfo TIDB_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); + + public static final Set TIDB_SYSTEM_DATABASES = + Sets.newHashSet("information_schema", "mysql", "performance_schema", "metrics_schema"); + + public static final OptionRule OPTION_RULE = + OptionRule.builder() + .required(TidbOptionRule.URL, TidbOptionRule.DRIVER) + .optional(TidbOptionRule.USER, TidbOptionRule.PASSWORD) + .build(); + + public static final OptionRule METADATA_RULE = + OptionRule.builder().required(TidbOptionRule.DATABASE, TidbOptionRule.TABLE).build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java new file mode 100644 index 000000000..8558f13c0 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.tidb.jdbc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class TidbJdbcDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return TidbDataSourceConfig.OPTION_RULE; + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return TidbDataSourceConfig.METADATA_RULE; + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + List tableNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[]{"TABLE"})) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + List dbNames = new ArrayList<>(); + try (Connection connection = getConnection(requestParams); + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { + // filter system databases + while (re.next()) { + String dbName = re.getString("database"); + if (StringUtils.isNotBlank(dbName) + && !TidbDataSourceConfig.TIDB_SYSTEM_DATABASES.contains(dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (SQLException | ClassNotFoundException e) { + throw new DataSourcePluginException("Get databases failed", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (Connection ignored = getConnection(requestParams)) { + return true; + } catch (Exception e) { + throw new DataSourcePluginException("check jdbc connectivity failed", e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + List tableFields = new ArrayList<>(); + try (Connection connection = getConnection(requestParams, database)) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, database, table); + try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) { + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString())); + tableFields.add(tableField); + } + } + } catch (ClassNotFoundException | SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + return tables.parallelStream() + .collect( + Collectors.toMap( + Function.identity(), + table -> + getTableFields( + pluginName, requestParams, database, table))); + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private Connection getConnection(Map requestParams) + throws SQLException, ClassNotFoundException { + return getConnection(requestParams, null); + } + + private Connection getConnection(Map requestParams, String databaseName) + throws SQLException, ClassNotFoundException { + checkNotNull(requestParams.get(TidbOptionRule.DRIVER.key())); + checkNotNull(requestParams.get(TidbOptionRule.URL.key()), "Jdbc url cannot be null"); + String url = + JdbcUtils.replaceDatabase( + requestParams.get(TidbOptionRule.URL.key()), databaseName); + if (requestParams.containsKey(TidbOptionRule.USER.key())) { + String username = requestParams.get(TidbOptionRule.USER.key()); + String password = requestParams.get(TidbOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceFactory.java new file mode 100644 index 000000000..49906bc95 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceFactory.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.tidb.jdbc; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; + +import java.util.Set; + +@Slf4j +@AutoService(DataSourceFactory.class) +public class TidbJdbcDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return TidbDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet(TidbDataSourceConfig.TIDB_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new TidbJdbcDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java new file mode 100644 index 000000000..90af0f95f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.tidb.jdbc; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; + +public class TidbOptionRule { + + public static final Option URL = + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static final Option USER = + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + + public static final Option PASSWORD = + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + + public static final Option DATABASE = + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + + public static final Option TABLE = + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + + public static final Option DRIVER = + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.MYSQL) + .withDescription("driver"); + + public enum DriverType { + MYSQL("com.mysql.cj.jdbc.Driver"), + ; + private final String driverClassName; + + DriverType(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getDriverClassName() { + return driverClassName; + } + + @Override + public String toString() { + return driverClassName; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/pom.xml new file mode 100644 index 000000000..64a905577 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/pom.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-kafka + + + 3.2.0 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + org.apache.kafka + kafka-clients + ${kafka.client.version} + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java new file mode 100644 index 000000000..72e679957 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.kafka; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.admin.DescribeClusterOptions; +import org.apache.kafka.clients.admin.DescribeClusterResult; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +public class KafkaDataSourceChannel implements DataSourceChannel { + + private static final String DATABASE = "default"; + private static final DescribeClusterOptions DEFAULT_TIMEOUT_OPTIONS = + new DescribeClusterOptions().timeoutMs(60 * 1000); + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return KafkaOptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return KafkaOptionRule.metadataRule(); + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default"); + try (AdminClient adminClient = createAdminClient(requestParams)) { + Set strings = adminClient.listTopics().names().get(); + return new ArrayList<>(strings); + } catch (Exception ex) { + throw new DataSourcePluginException( + "check kafka connectivity failed, " + ex.getMessage(), ex); + } + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + return DEFAULT_DATABASES; + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try (AdminClient adminClient = createAdminClient(requestParams)) { + // just test the connection + DescribeClusterResult describeClusterResult = + adminClient.describeCluster(DEFAULT_TIMEOUT_OPTIONS); + return CollectionUtils.isNotEmpty(describeClusterResult.nodes().get()); + } catch (Exception ex) { + throw new DataSourcePluginException( + "check kafka connectivity failed, " + ex.getMessage(), ex); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default"); + return Collections.emptyList(); + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default"); + return Collections.emptyMap(); + } + + private AdminClient createAdminClient(Map requestParams) { + return AdminClient.create( + KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams)); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java new file mode 100644 index 000000000..bcba81c0e --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.kafka; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; + +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class KafkaDataSourceFactory implements DataSourceFactory { + + public static final String KAFKA_PLUGIN_NAME = "Kafka"; + public static final String KAFKA_PLUGIN_ICON = "kafka"; + public static final String KAFKA_PLUGIN_VERSION = "1.0.0"; + + @Override + public String factoryIdentifier() { + return KAFKA_PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Sets.newHashSet( + DataSourcePluginInfo.builder() + .name(KAFKA_PLUGIN_NAME) + .icon(KAFKA_PLUGIN_ICON) + .version(KAFKA_PLUGIN_VERSION) + .supportVirtualTables(true) + .type(DatasourcePluginTypeEnum.NO_STRUCTURED.getCode()) + .build()); + } + + @Override + public DataSourceChannel createChannel() { + return new KafkaDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaOptionRule.java new file mode 100644 index 000000000..b599bb882 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaOptionRule.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.kafka; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +import java.util.Map; + +public class KafkaOptionRule { + + public static final Option BOOTSTRAP_SERVERS = + Options.key("bootstrap.servers") + .stringType() + .noDefaultValue() + .withDescription("Kafka cluster address, separated by \",\"."); + public static final Option TOPIC = + Options.key("topic") + .stringType() + .noDefaultValue() + .withDescription( + "Kafka topic name. If there are multiple topics, use , to split, for example: \"tpc1,tpc2\"."); + + public static final Option PATTERN = + Options.key("pattern") + .booleanType() + .defaultValue(false) + .withDescription( + "If pattern is set to true,the regular expression for a pattern of topic names to read from." + + " All topics in clients with names that match the specified regular expression will be subscribed by the consumer."); + + public static final Option> KAFKA_CONFIG = + Options.key("kafka.config") + .mapType() + .noDefaultValue() + .withDescription( + "{\n" + + "client.id=client_1\n" + + "max.poll.records=500\n" + + "auto.offset.reset=earliest\n" + + "enable.auto.commit=false\n" + + "}"); + + public static OptionRule optionRule() { + return OptionRule.builder().required(BOOTSTRAP_SERVERS).optional(KAFKA_CONFIG).build(); + } + + public static OptionRule metadataRule() { + return OptionRule.builder().required(TOPIC).optional(PATTERN).build(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java new file mode 100644 index 000000000..95988e762 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.kafka; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; + +import org.apache.kafka.clients.admin.AdminClientConfig; + +import java.util.Map; +import java.util.Properties; + +public class KafkaRequestParamsUtils { + + public static Properties parsePropertiesFromRequestParams(Map requestParams) { + checkArgument( + requestParams.containsKey(KafkaOptionRule.BOOTSTRAP_SERVERS.key()), + String.format( + "Missing %s in requestParams", KafkaOptionRule.BOOTSTRAP_SERVERS.key())); + final Properties properties = new Properties(); + properties.put( + AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, + requestParams.get(KafkaOptionRule.BOOTSTRAP_SERVERS.key())); + if (requestParams.containsKey(KafkaOptionRule.KAFKA_CONFIG.key())) { + Config configObject = + ConfigFactory.parseString( + requestParams.get(KafkaOptionRule.KAFKA_CONFIG.key())); + configObject + .entrySet() + .forEach( + entry -> { + properties.put( + entry.getKey(), entry.getValue().unwrapped().toString()); + }); + } + return properties; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java new file mode 100644 index 000000000..e694de45a --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.kafka; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import com.google.common.collect.ImmutableMap; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +// todo: use test container to test +@Slf4j +@Disabled +public class KafkaDataSourceChannelTest { + + private static final KafkaDataSourceChannel KAFKA_DATA_SOURCE_CHANNEL = + new KafkaDataSourceChannel(); + + private static final String KAFKA_PLUGIN_NAME = "kafka"; + private static final String BOOTSTRAP_SERVER = "localhost:9092"; + + private static final Map REQUEST_PARAMS = + new ImmutableMap.Builder() + .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), BOOTSTRAP_SERVER) + .build(); + + @Test + public void getDataSourceOptions() { + OptionRule dataSourceMetadataFieldsByDataSourceName = + KAFKA_DATA_SOURCE_CHANNEL.getDataSourceOptions(KAFKA_PLUGIN_NAME); + Assertions.assertEquals( + 1, dataSourceMetadataFieldsByDataSourceName.getRequiredOptions().size()); + } + + @Test + public void getDatasourceMetadataFieldsByDataSourceName() { + OptionRule datasourceMetadataFieldsByDataSourceName = + KAFKA_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName( + KAFKA_PLUGIN_NAME); + Assertions.assertEquals( + 2, datasourceMetadataFieldsByDataSourceName.getOptionalOptions().size()); + } + + @Test + public void getTables() { + List tables = + KAFKA_DATA_SOURCE_CHANNEL.getTables(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, null); + log.info("{}", tables); + Assertions.assertNotNull(tables); + } + + @Test + public void getDatabases() { + List databases = + KAFKA_DATA_SOURCE_CHANNEL.getDatabases(KAFKA_PLUGIN_NAME, REQUEST_PARAMS); + log.info("{}", databases); + Assertions.assertNotNull(databases); + } + + @Test + public void checkDataSourceConnectivity() { + boolean dataSourceConnectivity = + KAFKA_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity( + KAFKA_PLUGIN_NAME, REQUEST_PARAMS); + Assertions.assertTrue(dataSourceConnectivity); + } + + @Test + public void getTableFields() { + List tableFields = + KAFKA_DATA_SOURCE_CHANNEL.getTableFields(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", ""); + log.info("{}", tableFields); + Assertions.assertTrue(tableFields.isEmpty()); + } + + @Test + public void testGetTableFields() { + Map> tableFields = + KAFKA_DATA_SOURCE_CHANNEL.getTableFields( + KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", Collections.emptyList()); + log.info("{}", tableFields); + Assertions.assertTrue(tableFields.isEmpty()); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java new file mode 100644 index 000000000..67d8d33f8 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.kafka; + +import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +class KafkaRequestParamsUtilsTest { + + @Test + void parsePropertiesFromRequestParams() { + Map requestParams = + new ImmutableMap.Builder() + .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092") + .put( + KafkaOptionRule.KAFKA_CONFIG.key(), + "{" + "security.protocol = SASL_PLAINTEXT" + "}") + .build(); + Properties properties = + KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams); + Assertions.assertEquals("SASL_PLAINTEXT", properties.getProperty("security.protocol")); + } + + @Test + void parsePropertiesFromRequestParamsBadCase() { + Assertions.assertDoesNotThrow( + () -> + KafkaRequestParamsUtils.parsePropertiesFromRequestParams( + new ImmutableMap.Builder() + .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092") + .put(KafkaOptionRule.KAFKA_CONFIG.key(), "{}") + .build())); + + Assertions.assertThrows( + IllegalArgumentException.class, + () -> KafkaRequestParamsUtils.parsePropertiesFromRequestParams(new HashMap<>())); + + Assertions.assertDoesNotThrow( + () -> + KafkaRequestParamsUtils.parsePropertiesFromRequestParams( + new ImmutableMap.Builder() + .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092") + .put(KafkaOptionRule.KAFKA_CONFIG.key(), "") + .build())); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/pom.xml new file mode 100644 index 000000000..c7d565f84 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/pom.xml @@ -0,0 +1,57 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-mysql-cdc + + + 8.0.16 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + mysql + mysql-connector-java + ${mysql.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java new file mode 100644 index 000000000..07a9f3859 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.mysql; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import com.google.common.collect.Sets; +import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class MysqlCDCDataSourceChannel implements DataSourceChannel { + + public static final Set MYSQL_SYSTEM_DATABASES = + Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys"); + + @Override + public boolean canAbleGetSchema() { + return true; + } + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return MysqlCDCOptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return MysqlCDCOptionRule.metadataRule(); + } + + @Override + public List getTables( + String pluginName, Map requestParams, String database) { + return this.getTableNames(requestParams, database); + } + + @Override + public List getDatabases(String pluginName, Map requestParams) { + try { + return this.getDataBaseNames(requestParams); + } catch (SQLException e) { + throw new DataSourcePluginException("get databases failed", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + String pluginName, Map requestParams) { + return this.checkJdbcConnectivity(requestParams); + } + + @Override + public List getTableFields( + String pluginName, Map requestParams, String database, String table) { + return getTableFields(requestParams, database, table); + } + + @Override + public Map> getTableFields( + String pluginName, + Map requestParams, + String database, + List tables) { + Map> tableFields = new HashMap<>(tables.size()); + for (String table : tables) { + tableFields.put(table, getTableFields(requestParams, database, table)); + } + return tableFields; + } + + @SuppressWarnings("checkstyle:MagicNumber") + protected boolean checkJdbcConnectivity(Map requestParams) { + try (Connection connection = init(requestParams); + Statement statement = connection.createStatement()) { + + try (ResultSet resultSet = statement.executeQuery("SHOW MASTER STATUS");) { + if (resultSet.next()) { + String binlogFile = resultSet.getString("File"); + if (StringUtils.isBlank(binlogFile)) { + throw new DataSourcePluginException("binlog must be enabled"); + } + } else { + throw new DataSourcePluginException("binlog must be enabled"); + } + } + + try (ResultSet resultSet = + statement.executeQuery("SHOW VARIABLES LIKE 'binlog_format'")) { + if (resultSet.next()) { + String binlogFormat = resultSet.getString("Value"); + if (!"ROW".equalsIgnoreCase(binlogFormat)) { + throw new DataSourcePluginException("binlog_format must be ROW"); + } + } else { + throw new DataSourcePluginException("binlog_format must be ROW"); + } + } + + try (ResultSet resultSet = + statement.executeQuery("SHOW VARIABLES LIKE 'binlog_row_image'")) { + if (resultSet.next()) { + String binlogRowImage = resultSet.getString("Value"); + if (!"FULL".equalsIgnoreCase(binlogRowImage)) { + throw new DataSourcePluginException("binlog_row_image must be FULL"); + } + } else { + throw new DataSourcePluginException("binlog_row_image must be FULL"); + } + } + return true; + } catch (Exception e) { + throw new DataSourcePluginException( + "check jdbc connectivity failed, " + e.getMessage(), e); + } + } + + protected Connection init(Map requestParams) throws SQLException { + if (null == requestParams.get(MysqlCDCOptionRule.BASE_URL.key())) { + throw new DataSourcePluginException("Jdbc url is null"); + } + String url = requestParams.get(MysqlCDCOptionRule.BASE_URL.key()); + if (null != requestParams.get(MysqlCDCOptionRule.PASSWORD.key()) + && null != requestParams.get(MysqlCDCOptionRule.USERNAME.key())) { + String username = requestParams.get(MysqlCDCOptionRule.USERNAME.key()); + String password = requestParams.get(MysqlCDCOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } + + protected List getDataBaseNames(Map requestParams) throws SQLException { + List dbNames = new ArrayList<>(); + try (Connection connection = init(requestParams); + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { + // filter system databases + while (re.next()) { + String dbName = re.getString("database"); + if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } + } + + protected List getTableNames(Map requestParams, String dbName) { + List tableNames = new ArrayList<>(); + try (Connection connection = init(requestParams); + ResultSet resultSet = + connection + .getMetaData() + .getTables(dbName, null, null, new String[]{"TABLE"})) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + protected List getTableFields( + Map requestParams, String dbName, String tableName) { + List tableFields = new ArrayList<>(); + try (Connection connection = init(requestParams);) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, dbName, tableName); + ResultSet resultSet = metaData.getColumns(dbName, null, tableName, null); + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + boolean isNullable = convertToBoolean(nullable); + tableField.setNullable(isNullable); + tableFields.add(tableField); + } + } catch (SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private boolean isNotSystemDatabase(String dbName) { + return MYSQL_SYSTEM_DATABASES.stream() + .noneMatch( + systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName)); + } + + private boolean convertToBoolean(Object value) { + if (value instanceof Boolean) { + return (Boolean) value; + } + if (value instanceof String) { + return value.equals("TRUE"); + } + return false; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceConfig.java new file mode 100644 index 000000000..b81d43599 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceConfig.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.mysql; + +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +public class MysqlCDCDataSourceConfig { + + public static final String PLUGIN_NAME = "MySQL-CDC"; + + public static final DataSourcePluginInfo MYSQL_CDC_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceFactory.java new file mode 100644 index 000000000..ac4db000a --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceFactory.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.mysql; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; + +import java.util.Collections; +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class MysqlCDCDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return "MySQL-CDC"; + } + + @Override + public Set supportedDataSources() { + return Collections.singleton(MysqlCDCDataSourceConfig.MYSQL_CDC_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new MysqlCDCDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCOptionRule.java new file mode 100644 index 000000000..c0104c250 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCOptionRule.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.mysql; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +public class MysqlCDCOptionRule { + + public static final Option BASE_URL = + Options.key("base-url") + .stringType() + .noDefaultValue() + .withDescription( + "URL has to be without database, like \"jdbc:mysql://localhost:5432/\" or" + + "\"jdbc:mysql://localhost:5432\" rather than \"jdbc:mysql://localhost:5432/db\""); + + public static final Option USERNAME = + Options.key("username") + .stringType() + .noDefaultValue() + .withDescription( + "Name of the database to use when connecting to the database server."); + + public static final Option PASSWORD = + Options.key("password") + .stringType() + .noDefaultValue() + .withDescription("Password to use when connecting to the database server."); + + public static final Option DATABASE_NAME = + Options.key("database-name") + .stringType() + .noDefaultValue() + .withDescription("Database name of the database to monitor."); + + public static final Option TABLE_NAME = + Options.key("table-name") + .stringType() + .noDefaultValue() + .withDescription("Table name of the database to monitor."); + public static final Option SERVER_TIME_ZONE = + Options.key("server-time-zone") + .stringType() + .defaultValue("UTC") + .withDescription("The session time zone in database server."); + + public static OptionRule optionRule() { + return OptionRule.builder() + .required(USERNAME, PASSWORD, BASE_URL) + .optional(SERVER_TIME_ZONE) + .build(); + } + + public static OptionRule metadataRule() { + return OptionRule.builder().required(DATABASE_NAME, TABLE_NAME).build(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/pom.xml new file mode 100644 index 000000000..7bdd419df --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/pom.xml @@ -0,0 +1,37 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-plugins-api + + + + org.apache.seatunnel + seatunnel-api + + + com.google.auto.service + auto-service + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceChannel.java new file mode 100644 index 000000000..dade44577 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceChannel.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import com.google.common.collect.ImmutableList; +import lombok.NonNull; + +import java.util.List; +import java.util.Map; + +public interface DataSourceChannel { + + List DEFAULT_DATABASES = ImmutableList.of("default"); + + /** + * get datasource metadata fields by datasource name + * + * @param pluginName plugin name + * @return datasource metadata fields + */ + OptionRule getDataSourceOptions(@NonNull String pluginName); + + /** + * get datasource metadata fields by datasource name + * + * @param pluginName plugin name + * @return datasource metadata fields + */ + OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName); + + List getTables( + @NonNull String pluginName, Map requestParams, String database); + + List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams); + + boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams); + + default boolean canAbleGetSchema() { + return false; + } + + List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table); + + Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables); + + /** + * just check metadata field is right and used by virtual table + * + * @param requestParams request param(connector params) + * @return true if right + */ + default Boolean checkMetadataFieldIsRight(Map requestParams) { + return true; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceFactory.java new file mode 100644 index 000000000..966bb0330 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceFactory.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api; + +import java.util.Set; + +public interface DataSourceFactory { + + String factoryIdentifier(); + + Set supportedDataSources(); + + DataSourceChannel createChannel(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginException.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginException.java new file mode 100644 index 000000000..8913c4a48 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginException.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api; + +public class DataSourcePluginException extends RuntimeException { + + public DataSourcePluginException(String message) { + super(message); + } + + public DataSourcePluginException(String message, Throwable cause) { + super(message, cause); + } + + public DataSourcePluginException(Throwable cause) { + super(cause); + } + + public DataSourcePluginException() { + super(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java new file mode 100644 index 000000000..d496945de --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api; + +import static com.google.common.base.Preconditions.checkNotNull; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class DataSourcePluginInfo { + + public String name; + + public String icon; + + public String version; + + /** + * @see DatasourcePluginTypeEnum + */ + private Integer type; + + /** + * whether support virtual tables, default false + */ + private Boolean supportVirtualTables; + + public DataSourcePluginInfo( + String name, String icon, String version, Integer type, Boolean supportVirtualTables) { + this.name = checkNotNull(name, "name can not be null"); + this.icon = checkNotNull(icon, "icon can not be null"); + this.version = checkNotNull(version, "version can not be null"); + this.type = checkNotNull(type, "type can not be null"); + this.supportVirtualTables = supportVirtualTables != null && supportVirtualTables; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DatasourcePluginTypeEnum.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DatasourcePluginTypeEnum.java new file mode 100644 index 000000000..e5d8e0049 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DatasourcePluginTypeEnum.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api; + +import static com.google.common.base.Preconditions.checkNotNull; + +@SuppressWarnings("checkstyle:RegexpSingleline") +public enum DatasourcePluginTypeEnum { + DATABASE(1, "database", "传统数据库"), + FILE(2, "file", "文件"), + NO_STRUCTURED(3, "no_structured", "非结构化数据(NoSQLs)"), + STORAGE(4, "storage", "存储"), + REMOTE_CONNECTION(5, "remote_connection", "远程连接"); + + private final Integer code; + + private final String name; + + private final String chineseName; + + DatasourcePluginTypeEnum(Integer code, String name, String chineseName) { + this.code = checkNotNull(code); + this.name = checkNotNull(name); + this.chineseName = checkNotNull(chineseName); + } + + public Integer getCode() { + return code; + } + + public String getName() { + return name; + } + + public String getChineseName() { + return chineseName; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java new file mode 100644 index 000000000..2a27a5684 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api.common; + +import org.apache.seatunnel.common.utils.JsonUtils; + +import java.util.Map; + +public class ParamtersUtils { + /** + * for some parameters, we need to convert them to {@link Map} eg: s3Options { + * "access.value": "org.apache.hadoop.fs.s3a.S3AFileSystem", "access.key": + * "AKIAIOSFODNN7EXAMPLE", "hadoop_s3_properties": " fs.s3a.impl = + * org.apache.hadoop.fs.s3a.S3AFileSystem fs.s3a.access.key = AKIAIOSFODNN7EXAMPLE " + * + *

Convert parameters to {@link Map} + * + * @param parameters parameters {@link Map} + * @return {@link Map} + */ + public static Map convertParams(Map parameters) { + String json = JsonUtils.toJsonString(parameters); + return JsonUtils.toMap(json, String.class, Object.class); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/model/TableField.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/model/TableField.java new file mode 100644 index 000000000..fabcaf478 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/model/TableField.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api.model; + +import lombok.Data; + +import java.util.Map; + +@Data +public class TableField { + + private String type; + + private String name; + + private String comment; + + private Boolean primaryKey; + + private String defaultValue; + + private Boolean nullable; + + private Map properties; +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/utils/JdbcUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/utils/JdbcUtils.java new file mode 100644 index 000000000..8e7cb3d85 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/utils/JdbcUtils.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api.utils; + +public class JdbcUtils { + + public static String replaceDatabase(String jdbcUrl, String databaseName) { + if (databaseName == null) { + return jdbcUrl; + } + String[] split = jdbcUrl.split("\\?"); + if (split.length == 1) { + return replaceDatabaseWithoutParameter(jdbcUrl, databaseName); + } + return replaceDatabaseWithoutParameter(split[0], databaseName) + "?" + split[1]; + } + + private static String replaceDatabaseWithoutParameter(String jdbcUrl, String databaseName) { + int lastIndex = jdbcUrl.lastIndexOf(':'); + char[] chars = jdbcUrl.toCharArray(); + for (int i = lastIndex + 1; i < chars.length; i++) { + if (chars[i] == '/') { + return jdbcUrl.substring(0, i + 1) + databaseName; + } + } + return jdbcUrl + "/" + databaseName; + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/test/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtilsTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/test/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtilsTest.java new file mode 100644 index 000000000..98b8d7ee7 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/test/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtilsTest.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.api.common; + +import org.apache.seatunnel.common.utils.JsonUtils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +public class ParamtersUtilsTest { + + @Test + public void testConvertParams() { + Map s3Options = new HashMap<>(); + s3Options.put("access.key", "myaccess"); + s3Options.put("access.value", "myvalue"); + Map hadoopConfig = new HashMap<>(); + hadoopConfig.put("fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem"); + s3Options.put("hadoopConfig", hadoopConfig); + String s3OptionsJson = JsonUtils.toJsonString(s3Options); + Map s3OptionsMap = + JsonUtils.toMap(s3OptionsJson, String.class, String.class); + + Map s3OptionsMapConvertResult = ParamtersUtils.convertParams(s3OptionsMap); + Assertions.assertEquals(s3OptionsMapConvertResult.get("hadoopConfig"), hadoopConfig); + Assertions.assertEquals( + s3OptionsMapConvertResult.get("access.key"), s3Options.get("access.key")); + Assertions.assertEquals( + s3OptionsMapConvertResult.get("access.value"), s3Options.get("access.value")); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/pom.xml new file mode 100644 index 000000000..3f7e0e6e7 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-s3-redshift + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.seatunnel + seatunnel-hadoop3-3.1.4-uber + ${seatunnel-framework.version} + + + org.apache.avro + avro + + + + + com.amazon.redshift + redshift-jdbc42 + ${redshift.version} + + + org.apache.hadoop + hadoop-aws + ${hadoop-aws.version} + + + jdk.tools + jdk.tools + + + + + com.amazonaws + aws-java-sdk-bundle + ${aws-java-sdk-bundle.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java new file mode 100644 index 000000000..f32561867 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.s3; + +import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.conf.Configuration; + +import java.util.Arrays; +import java.util.Map; + +@Slf4j +public class HadoopS3AConfiguration { + + /* S3 constants */ + private static final String S3A_SCHEMA = "s3a"; + private static final String HDFS_S3N_IMPL = "org.apache.hadoop.fs.s3native.NativeS3FileSystem"; + private static final String HDFS_S3A_IMPL = "org.apache.hadoop.fs.s3a.S3AFileSystem"; + private static final String S3A_PROTOCOL = "s3a"; + private static final String DEFAULT_PROTOCOL = "s3n"; + private static final String S3_FORMAT_KEY = "fs.%s.%s"; + private static final String HDFS_IMPL_KEY = "impl"; + + public static Configuration getConfiguration(Map s3Options) { + + if (!s3Options.containsKey(S3RedshiftOptionRule.BUCKET.key())) { + throw new IllegalArgumentException( + "S3Redshift datasource bucket is null, please check your config"); + } + if (!s3Options.containsKey(S3RedshiftOptionRule.FS_S3A_ENDPOINT.key())) { + throw new IllegalArgumentException( + "S3Redshift datasource endpoint is null, please check your config"); + } + String bucket = s3Options.get(S3RedshiftOptionRule.BUCKET.key()); + + String protocol = DEFAULT_PROTOCOL; + if (bucket.startsWith(S3A_PROTOCOL)) { + protocol = S3A_PROTOCOL; + } + String fsImpl = protocol.equals(S3A_PROTOCOL) ? HDFS_S3A_IMPL : HDFS_S3N_IMPL; + Configuration hadoopConf = new Configuration(); + hadoopConf.set(FS_DEFAULT_NAME_KEY, bucket); + hadoopConf.set( + S3RedshiftOptionRule.FS_S3A_ENDPOINT.key(), + s3Options.get(S3RedshiftOptionRule.FS_S3A_ENDPOINT.key())); + hadoopConf.set(formatKey(protocol, HDFS_IMPL_KEY), fsImpl); + if (s3Options.containsKey(S3RedshiftOptionRule.HADOOP_S3_PROPERTIES.key())) { + Arrays.stream( + s3Options + .get(S3RedshiftOptionRule.HADOOP_S3_PROPERTIES.key()) + .split("\n")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .forEach( + line -> { + String[] kv = line.split("="); + if (kv.length == 2) { + hadoopConf.set(kv[0].trim(), kv[1].trim()); + } + }); + } + if (S3RedshiftOptionRule.S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider + .getProvider() + .equals(s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) { + hadoopConf.set( + S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + hadoopConf.set( + "fs.s3a.access.key", s3Options.get(S3RedshiftOptionRule.ACCESS_KEY.key())); + hadoopConf.set( + "fs.s3a.secret.key", s3Options.get(S3RedshiftOptionRule.SECRET_KEY.key())); + } else { + hadoopConf.set( + S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + } + return hadoopConf; + } + + private static String formatKey(String protocol, String key) { + return String.format(S3_FORMAT_KEY, protocol, key); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java new file mode 100644 index 000000000..07aceb327 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.s3; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; + +import com.google.common.collect.Sets; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +public class S3RedshiftDataSourceChannel implements DataSourceChannel { + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return S3RedshiftOptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return S3RedshiftOptionRule.metadataRule(); + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + return getTableNames(requestParams, database); + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + try { + return getDataBaseNames(pluginName, requestParams); + } catch (SQLException e) { + throw new DataSourcePluginException("Query redshift databases failed", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + checkHdfsS3Connection(requestParams); + checkJdbcConnection(requestParams); + return true; + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + return getTableFields(requestParams, database, table); + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + // not need this method + return null; + } + + private void checkJdbcConnection(Map requestParams) { + String jdbcUrl = requestParams.get(S3RedshiftOptionRule.JDBC_URL.key()); + String username = requestParams.get(S3RedshiftOptionRule.JDBC_USER.key()); + String password = requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key()); + if (StringUtils.isBlank(jdbcUrl)) { + throw new DataSourcePluginException("Redshift Jdbc url is empty"); + } + if (StringUtils.isBlank(username) && StringUtils.isBlank(password)) { + try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { + log.info("Redshift jdbc connection is valid"); + return; + } catch (SQLException e) { + throw new DataSourcePluginException( + "Check Redshift jdbc connection failed,please check your config", e); + } + } + try (Connection ignored = DriverManager.getConnection(jdbcUrl, username, password)) { + log.info("Redshift jdbc connection is valid"); + } catch (SQLException e) { + throw new DataSourcePluginException( + "Check Redshift jdbc connection failed,please check your config", e); + } + } + + private void checkHdfsS3Connection(Map requestParams) { + Configuration s3Conf = HadoopS3AConfiguration.getConfiguration(requestParams); + try (FileSystem fs = FileSystem.get(s3Conf)) { + fs.getFileStatus(new org.apache.hadoop.fs.Path("/")); + } catch (IOException e) { + throw new DataSourcePluginException( + "S3 configuration is invalid, please check your config", e); + } + } + + protected Connection init(Map requestParams, String databaseName) + throws SQLException { + if (null == requestParams.get(S3RedshiftOptionRule.JDBC_URL.key())) { + throw new DataSourcePluginException("Jdbc url is null"); + } + String url = + JdbcUtils.replaceDatabase( + requestParams.get(S3RedshiftOptionRule.JDBC_URL.key()), databaseName); + if (null != requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key()) + && null != requestParams.get(S3RedshiftOptionRule.JDBC_USER.key())) { + String username = requestParams.get(S3RedshiftOptionRule.JDBC_USER.key()); + String password = requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } + + protected List getDataBaseNames(String pluginName, Map requestParams) + throws SQLException { + List dbNames = new ArrayList<>(); + try (Connection connection = init(requestParams, null); + PreparedStatement statement = + connection.prepareStatement("select datname from pg_database;"); + ResultSet re = statement.executeQuery()) { + while (re.next()) { + String dbName = re.getString("datname"); + if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(dbName)) { + dbNames.add(dbName); + } + } + return dbNames; + } catch (SQLException e) { + throw new DataSourcePluginException("get databases failed", e); + } + } + + protected List getTableNames(Map requestParams, String dbName) { + List tableNames = new ArrayList<>(); + try (Connection connection = init(requestParams, dbName);) { + ResultSet resultSet = + connection.getMetaData().getTables(dbName, null, null, new String[]{"TABLE"}); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (StringUtils.isNotBlank(tableName)) { + tableNames.add(tableName); + } + } + return tableNames; + } catch (SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + protected List getTableFields( + Map requestParams, String dbName, String tableName) { + List tableFields = new ArrayList<>(); + try (Connection connection = init(requestParams, dbName);) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, dbName, tableName); + String[] split = tableName.split("\\."); + if (split.length != 2) { + throw new DataSourcePluginException( + "Postgresql tableName should composed by schemaName.tableName"); + } + ResultSet resultSet = metaData.getColumns(dbName, split[0], split[1], null); + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + boolean isNullable = convertToBoolean(nullable); + tableField.setNullable(isNullable); + tableFields.add(tableField); + } + } catch (SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + @SuppressWarnings("checkstyle:MagicNumber") + private static boolean checkHostConnectable(String host, int port) { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(host, port), 1000); + return true; + } catch (IOException e) { + + throw new DataSourcePluginException("check host connectable failed", e); + } + } + + private boolean isNotSystemDatabase(String dbName) { + return !POSTGRESQL_SYSTEM_DATABASES.contains(dbName.toLowerCase()); + } + + private boolean convertToBoolean(Object value) { + if (value instanceof Boolean) { + return (Boolean) value; + } + if (value instanceof String) { + return value.equals("TRUE"); + } + return false; + } + + public static final Set POSTGRESQL_SYSTEM_DATABASES = + Sets.newHashSet( + "information_schema", + "pg_catalog", + "root", + "pg_toast", + "pg_temp_1", + "pg_toast_temp_1", + "postgres", + "template0", + "template1"); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java new file mode 100644 index 000000000..3ef212acd --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.s3; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; + +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class S3RedshiftDataSourceFactory implements DataSourceFactory { + @Override + public String factoryIdentifier() { + return "S3-Redshift"; + } + + @Override + public Set supportedDataSources() { + DataSourcePluginInfo s3DatasourcePluginInfo = + DataSourcePluginInfo.builder() + .name("S3-Redshift") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .version("1.0.0") + .supportVirtualTables(false) + .icon("S3-Redshift") + .icon("S3-Redshift") + .build(); + + return Sets.newHashSet(s3DatasourcePluginInfo); + } + + @Override + public DataSourceChannel createChannel() { + return new S3RedshiftDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftOptionRule.java new file mode 100644 index 000000000..5d69ee803 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftOptionRule.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.redshift.s3; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +import java.util.Arrays; +import java.util.Map; + +public class S3RedshiftOptionRule { + + public static final Option JDBC_URL = + Options.key("jdbc_url") + .stringType() + .noDefaultValue() + .withDescription( + "Redshift jdbc connection url, eg: jdbc:redshift://localhost:5439/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + + public static final Option JDBC_USER = + Options.key("jdbc_user") + .stringType() + .noDefaultValue() + .withDescription("Redshift jdbc connection user"); + + public static final Option JDBC_PASSWORD = + Options.key("jdbc_password") + .stringType() + .noDefaultValue() + .withDescription("Redshift jdbc connection password"); + + public static final Option ACCESS_KEY = + Options.key("access_key") + .stringType() + .noDefaultValue() + .withDescription("S3 access key"); + + public static final Option SECRET_KEY = + Options.key("secret_key") + .stringType() + .noDefaultValue() + .withDescription("S3 secret key"); + + public static final Option BUCKET = + Options.key("bucket").stringType().noDefaultValue().withDescription("S3 bucket name"); + + public static final Option FS_S3A_ENDPOINT = + Options.key("fs.s3a.endpoint") + .stringType() + .noDefaultValue() + .withDescription("fs s3a endpoint"); + + public static final Option S3A_AWS_CREDENTIALS_PROVIDER = + Options.key("fs.s3a.aws.credentials.provider") + .enumType(S3aAwsCredentialsProvider.class) + .defaultValue(S3aAwsCredentialsProvider.InstanceProfileCredentialsProvider) + .withDescription("s3a aws credentials provider"); + + public static final Option> HADOOP_S3_PROPERTIES = + Options.key("hadoop_s3_properties") + .mapType() + .noDefaultValue() + .withDescription( + "{" + + "fs.s3a.buffer.dir = /data/st_test/s3a\n" + + "fs.s3a.fast.upload.buffer = disk\n" + + "}"); + + public static OptionRule optionRule() { + return OptionRule.builder() + .required( + JDBC_URL, + BUCKET, + FS_S3A_ENDPOINT, + S3A_AWS_CREDENTIALS_PROVIDER, + JDBC_USER, + JDBC_PASSWORD) + .optional(HADOOP_S3_PROPERTIES) + .conditional( + S3A_AWS_CREDENTIALS_PROVIDER, + S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider, + ACCESS_KEY, + SECRET_KEY) + .build(); + } + + public static final Option PATH = + Options.key("path").stringType().noDefaultValue().withDescription("S3 write path"); + + public static final Option TYPE = + Options.key("file_format_type") + .enumType(FileFormat.class) + .noDefaultValue() + .withDescription("S3 write type"); + + public static final Option DELIMITER = + Options.key("delimiter") + .stringType() + .noDefaultValue() + .withDescription("S3 write delimiter"); + + public static final Option> SCHEMA = + Options.key("schema").mapType().noDefaultValue().withDescription("SeaTunnel Schema"); + + public static final Option PARSE_PARSE_PARTITION_FROM_PATH = + Options.key("parse_partition_from_path") + .booleanType() + .noDefaultValue() + .withDescription("S3 write parse_partition_from_path"); + + public static final Option DATE_FORMAT = + Options.key("date_format") + .stringType() + .noDefaultValue() + .withDescription("S3 write date_format"); + + public static final Option DATETIME_FORMAT = + Options.key("time_format") + .stringType() + .noDefaultValue() + .withDescription("S3 write time_format"); + + public static final Option TIME_FORMAT = + Options.key("datetime_format") + .stringType() + .noDefaultValue() + .withDescription("S3 write datetime_format"); + + public static OptionRule metadataRule() { + return OptionRule.builder() + .required(PATH, TYPE) + .conditional(TYPE, FileFormat.TEXT, DELIMITER) + .conditional(TYPE, Arrays.asList(FileFormat.TEXT, FileFormat.JSON), SCHEMA) + .optional(PARSE_PARSE_PARTITION_FROM_PATH) + .optional(DATE_FORMAT) + .optional(DATETIME_FORMAT) + .optional(TIME_FORMAT) + .build(); + } + + public enum S3aAwsCredentialsProvider { + SimpleAWSCredentialsProvider("org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider"), + + InstanceProfileCredentialsProvider("com.amazonaws.auth.InstanceProfileCredentialsProvider"); + + private String provider; + + S3aAwsCredentialsProvider(String provider) { + this.provider = provider; + } + + public String getProvider() { + return provider; + } + + @Override + public String toString() { + return provider; + } + } + + public enum FileFormat { + CSV("csv"), + TEXT("txt"), + PARQUET("parquet"), + ORC("orc"), + JSON("json"); + + private final String type; + + FileFormat(String type) { + this.type = type; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml new file mode 100644 index 000000000..be117f58c --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml @@ -0,0 +1,68 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-s3 + + + 2.4.4-WS-SNAPSHOT + 3.1.4 + 1.11.271 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.seatunnel + seatunnel-hadoop3-3.1.4-uber + ${hadoop-uber.version} + + + org.apache.avro + avro + + + + + org.apache.hadoop + hadoop-aws + ${hadoop-aws.version} + + + jdk.tools + jdk.tools + + + + + com.amazonaws + aws-java-sdk-bundle + ${aws-java-sdk-bundle.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java new file mode 100644 index 000000000..ecce9793c --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.s3; + +import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; + +import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.conf.Configuration; + +import java.util.Map; + +@Slf4j +public class HadoopS3AConfiguration { + + /* S3 constants */ + private static final String S3A_SCHEMA = "s3a"; + private static final String HDFS_S3N_IMPL = "org.apache.hadoop.fs.s3native.NativeS3FileSystem"; + private static final String HDFS_S3A_IMPL = "org.apache.hadoop.fs.s3a.S3AFileSystem"; + private static final String S3A_PROTOCOL = "s3a"; + private static final String DEFAULT_PROTOCOL = "s3n"; + private static final String S3_FORMAT_KEY = "fs.%s.%s"; + private static final String HDFS_IMPL_KEY = "impl"; + + public static Configuration getConfiguration(Map s3Options) { + + if (!s3Options.containsKey(S3OptionRule.BUCKET.key())) { + throw new IllegalArgumentException( + "S3 datasource bucket is null, please check your config"); + } + if (!s3Options.containsKey(S3OptionRule.FS_S3A_ENDPOINT.key())) { + throw new IllegalArgumentException( + "S3 datasource endpoint is null, please check your config"); + } + String bucket = s3Options.get(S3OptionRule.BUCKET.key()); + + String protocol = DEFAULT_PROTOCOL; + if (bucket.startsWith(S3A_PROTOCOL)) { + protocol = S3A_PROTOCOL; + } + String fsImpl = protocol.equals(S3A_PROTOCOL) ? HDFS_S3A_IMPL : HDFS_S3N_IMPL; + Configuration hadoopConf = new Configuration(); + hadoopConf.set(FS_DEFAULT_NAME_KEY, bucket); + hadoopConf.set( + S3OptionRule.FS_S3A_ENDPOINT.key(), + s3Options.get(S3OptionRule.FS_S3A_ENDPOINT.key())); + hadoopConf.set(formatKey(protocol, HDFS_IMPL_KEY), fsImpl); + if (s3Options.containsKey(S3OptionRule.HADOOP_S3_PROPERTIES.key())) { + Config configObject = + ConfigFactory.parseString( + s3Options.get(S3OptionRule.HADOOP_S3_PROPERTIES.key())); + configObject + .entrySet() + .forEach( + entry -> { + hadoopConf.set( + entry.getKey(), entry.getValue().unwrapped().toString()); + }); + } + if (S3OptionRule.S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider.getProvider() + .equals(s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) { + hadoopConf.set( + S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + hadoopConf.set("fs.s3a.access.key", s3Options.get(S3OptionRule.ACCESS_KEY.key())); + hadoopConf.set("fs.s3a.secret.key", s3Options.get(S3OptionRule.SECRET_KEY.key())); + } else { + hadoopConf.set( + S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + } + return hadoopConf; + } + + private static String formatKey(String protocol, String key) { + return String.format(S3_FORMAT_KEY, protocol, key); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java new file mode 100644 index 000000000..640b6f16f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.s3; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +import com.google.auto.service.AutoService; +import com.google.common.collect.Sets; + +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class S3DataSourceFactory implements DataSourceFactory { + + private static final String PLUGIN_NAME = "S3"; + + @Override + public String factoryIdentifier() { + return PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + DataSourcePluginInfo s3DatasourcePluginInfo = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .type(DatasourcePluginTypeEnum.FILE.getCode()) + .version("1.0.0") + .supportVirtualTables(false) + .icon("S3File") + .build(); + + return Sets.newHashSet(s3DatasourcePluginInfo); + } + + @Override + public DataSourceChannel createChannel() { + return new S3DatasourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java new file mode 100644 index 000000000..e1ba8de5e --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.s3; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import lombok.NonNull; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class S3DatasourceChannel implements DataSourceChannel { + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return S3OptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return S3OptionRule.metadataRule(); + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + throw new UnsupportedOperationException("getTables is not supported for S3 datasource"); + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + throw new UnsupportedOperationException("getDatabases is not supported for S3 datasource"); + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + Configuration conf = HadoopS3AConfiguration.getConfiguration(requestParams); + try (FileSystem fs = FileSystem.get(conf)) { + fs.listStatus(new Path("/")); + return true; + } catch (IOException e) { + throw new DataSourcePluginException( + String.format("check s3 connectivity failed, config is: %s", requestParams), e); + } + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + throw new UnsupportedOperationException( + "getTableFields is not supported for S3 datasource"); + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + throw new UnsupportedOperationException( + "getTableFields is not supported for S3 datasource"); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3OptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3OptionRule.java new file mode 100644 index 000000000..ba56f303a --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3OptionRule.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.s3; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +import java.util.Arrays; +import java.util.Map; + +public class S3OptionRule { + + public static final Option ACCESS_KEY = + Options.key("access_key") + .stringType() + .noDefaultValue() + .withDescription("S3 access key"); + + public static final Option SECRET_KEY = + Options.key("secret_key") + .stringType() + .noDefaultValue() + .withDescription("S3 secret key"); + + public static final Option BUCKET = + Options.key("bucket").stringType().noDefaultValue().withDescription("S3 bucket name"); + + public static final Option FS_S3A_ENDPOINT = + Options.key("fs.s3a.endpoint") + .stringType() + .noDefaultValue() + .withDescription("fs s3a endpoint"); + + public static final Option S3A_AWS_CREDENTIALS_PROVIDER = + Options.key("fs.s3a.aws.credentials.provider") + .enumType(S3aAwsCredentialsProvider.class) + .defaultValue(S3aAwsCredentialsProvider.InstanceProfileCredentialsProvider) + .withDescription("s3a aws credentials provider"); + + public static final Option> HADOOP_S3_PROPERTIES = + Options.key("hadoop_s3_properties") + .mapType() + .noDefaultValue() + .withDescription( + "{\n" + + "fs.s3a.buffer.dir=/data/st_test/s3a\n" + + "fs.s3a.fast.upload.buffer=disk\n" + + "}"); + + public static OptionRule optionRule() { + return OptionRule.builder() + .required(BUCKET, FS_S3A_ENDPOINT, S3A_AWS_CREDENTIALS_PROVIDER) + .optional(HADOOP_S3_PROPERTIES) + .conditional( + S3A_AWS_CREDENTIALS_PROVIDER, + S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider, + ACCESS_KEY, + SECRET_KEY) + .build(); + } + + public static final Option PATH = + Options.key("path").stringType().noDefaultValue().withDescription("S3 write path"); + + public static final Option TYPE = + Options.key("file_format_type") + .enumType(FileFormat.class) + .noDefaultValue() + .withDescription("S3 write type"); + + public static final Option DELIMITER = + Options.key("delimiter") + .stringType() + .noDefaultValue() + .withDescription("S3 write delimiter"); + + public static final Option> SCHEMA = + Options.key("schema").mapType().noDefaultValue().withDescription("SeaTunnel Schema"); + + public static final Option PARSE_PARSE_PARTITION_FROM_PATH = + Options.key("parse_partition_from_path") + .booleanType() + .noDefaultValue() + .withDescription("S3 write parse_partition_from_path"); + + public static final Option DATE_FORMAT = + Options.key("date_format") + .stringType() + .noDefaultValue() + .withDescription("S3 write date_format"); + + public static final Option DATETIME_FORMAT = + Options.key("time_format") + .stringType() + .noDefaultValue() + .withDescription("S3 write time_format"); + + public static final Option TIME_FORMAT = + Options.key("datetime_format") + .stringType() + .noDefaultValue() + .withDescription("S3 write datetime_format"); + + public static OptionRule metadataRule() { + return OptionRule.builder() + .required(PATH, TYPE) + .conditional(TYPE, FileFormat.TEXT, DELIMITER) + .conditional(TYPE, Arrays.asList(FileFormat.TEXT, FileFormat.JSON), SCHEMA) + .optional(PARSE_PARSE_PARTITION_FROM_PATH) + .optional(DATE_FORMAT) + .optional(DATETIME_FORMAT) + .optional(TIME_FORMAT) + .build(); + } + + public enum S3aAwsCredentialsProvider { + SimpleAWSCredentialsProvider("org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider"), + + InstanceProfileCredentialsProvider("com.amazonaws.auth.InstanceProfileCredentialsProvider"); + + private String provider; + + S3aAwsCredentialsProvider(String provider) { + this.provider = provider; + } + + public String getProvider() { + return provider; + } + + @Override + public String toString() { + return provider; + } + } + + public enum FileFormat { + CSV("csv"), + TEXT("txt"), + PARQUET("parquet"), + ORC("orc"), + JSON("json"); + + private final String type; + + FileFormat(String type) { + this.type = type; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/pom.xml new file mode 100644 index 000000000..467b79146 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-sqlserver-cdc + + + 9.2.1.jre8 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.seatunnel + seatunnel-api + + + com.google.auto.service + auto-service + + + + com.microsoft.sqlserver + mssql-jdbc + ${sqlserver.version} + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java new file mode 100644 index 000000000..4fed9948f --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.sqlserver; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import com.google.common.collect.Sets; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +public class SqlServerCDCDataSourceChannel implements DataSourceChannel { + + public static final Set MYSQL_SYSTEM_DATABASES = + Sets.newHashSet("master", "tempdb", "model", "msdb"); + + @Override + public boolean canAbleGetSchema() { + return true; + } + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return SqlServerCDCOptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return SqlServerCDCOptionRule.metadataRule(); + } + + @Override + public List getTables( + String pluginName, Map requestParams, String database) { + return this.getTableNames(requestParams, database); + } + + @Override + public List getDatabases(String pluginName, Map requestParams) { + try { + return this.getDataBaseNames(requestParams); + } catch (SQLException e) { + throw new DataSourcePluginException("get databases failed", e); + } + } + + @Override + public boolean checkDataSourceConnectivity( + String pluginName, Map requestParams) { + try (Connection connection = init(requestParams); + PreparedStatement statement = connection.prepareStatement("SELECT 1"); + ResultSet rs = statement.executeQuery()) { + return rs.next(); + } catch (SQLException e) { + throw new DataSourcePluginException("connect datasource failed", e); + } + } + + @Override + public List getTableFields( + String pluginName, Map requestParams, String database, String table) { + Pair pair = parseSchemaAndTable(table); + return getTableFields(requestParams, database, pair.getLeft(), pair.getRight()); + } + + @Override + public Map> getTableFields( + String pluginName, + Map requestParams, + String database, + List tables) { + Map> tableFields = new HashMap<>(tables.size()); + for (String table : tables) { + tableFields.put(table, getTableFields(pluginName, requestParams, database, table)); + } + return tableFields; + } + + private Connection init(Map requestParams) throws SQLException { + if (null == requestParams.get(SqlServerCDCOptionRule.BASE_URL.key())) { + throw new DataSourcePluginException("Jdbc url is null"); + } + String url = requestParams.get(SqlServerCDCOptionRule.BASE_URL.key()); + if (null != requestParams.get(SqlServerCDCOptionRule.PASSWORD.key()) + && null != requestParams.get(SqlServerCDCOptionRule.USERNAME.key())) { + String username = requestParams.get(SqlServerCDCOptionRule.USERNAME.key()); + String password = requestParams.get(SqlServerCDCOptionRule.PASSWORD.key()); + return DriverManager.getConnection(url, username, password); + } + return DriverManager.getConnection(url); + } + + private List getDataBaseNames(Map requestParams) throws SQLException { + List dbNames = new ArrayList<>(); + try (Connection connection = init(requestParams); + PreparedStatement statement = + connection.prepareStatement( + "SELECT NAME FROM SYS.DATABASES WHERE IS_CDC_ENABLED = 1;"); + ResultSet re = statement.executeQuery()) { + // filter system databases + while (re.next()) { + String dbName = re.getString("NAME"); + if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(dbName)) { + dbNames.add(dbName); + } + } + + return dbNames; + } + } + + private List getTableNames(Map requestParams, String dbName) { + final String sql = + String.format( + "SELECT SCHEMAS.NAME AS SCHEMA_NAME, TABLES.NAME AS TABLE_NAME" + + " FROM %s.SYS.SCHEMAS AS SCHEMAS" + + " JOIN %s.SYS.TABLES AS TABLES" + + " ON SCHEMAS.SCHEMA_ID = TABLES.SCHEMA_ID" + + " AND TABLES.IS_TRACKED_BY_CDC = 1", + dbName, dbName); + + List tableNames = new ArrayList<>(); + try (Connection connection = init(requestParams); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { + while (resultSet.next()) { + String schemaName = resultSet.getString("SCHEMA_NAME"); + String tableName = resultSet.getString("TABLE_NAME"); + tableNames.add(schemaName + "." + tableName); + } + return tableNames; + } catch (SQLException e) { + throw new DataSourcePluginException("get table names failed", e); + } + } + + private List getTableFields( + Map requestParams, String dbName, String schemaName, String tableName) { + List tableFields = new ArrayList<>(); + try (Connection connection = init(requestParams);) { + DatabaseMetaData metaData = connection.getMetaData(); + String primaryKey = getPrimaryKey(metaData, dbName, schemaName, tableName); + ResultSet resultSet = metaData.getColumns(dbName, schemaName, tableName, null); + while (resultSet.next()) { + TableField tableField = new TableField(); + String columnName = resultSet.getString("COLUMN_NAME"); + tableField.setPrimaryKey(false); + if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) { + tableField.setPrimaryKey(true); + } + tableField.setName(columnName); + tableField.setType(resultSet.getString("TYPE_NAME")); + tableField.setComment(resultSet.getString("REMARKS")); + Object nullable = resultSet.getObject("IS_NULLABLE"); + boolean isNullable = convertToBoolean(nullable); + tableField.setNullable(isNullable); + tableFields.add(tableField); + } + } catch (SQLException e) { + throw new DataSourcePluginException("get table fields failed", e); + } + return tableFields; + } + + private String getPrimaryKey( + DatabaseMetaData metaData, String dbName, String schemaName, String tableName) + throws SQLException { + ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, schemaName, tableName); + while (primaryKeysInfo.next()) { + return primaryKeysInfo.getString("COLUMN_NAME"); + } + return null; + } + + private boolean isNotSystemDatabase(String dbName) { + return MYSQL_SYSTEM_DATABASES.stream() + .noneMatch( + systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName)); + } + + private boolean convertToBoolean(Object value) { + if (value instanceof Boolean) { + return (Boolean) value; + } + if (value instanceof String) { + return value.equals("TRUE"); + } + return false; + } + + private Pair parseSchemaAndTable(String tableName) { + String[] schemaAndTable = tableName.split("\\."); + if (schemaAndTable.length != 2) { + throw new DataSourcePluginException("table name is invalid"); + } + return Pair.of(schemaAndTable[0], schemaAndTable[1]); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceConfig.java new file mode 100644 index 000000000..4da5b9d57 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceConfig.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.sqlserver; + +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +public class SqlServerCDCDataSourceConfig { + + public static final String PLUGIN_NAME = "SqlServer-CDC"; + + public static final DataSourcePluginInfo SQLSERVER_CDC_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java new file mode 100644 index 000000000..321f9c8c6 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.sqlserver; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; + +import java.util.Collections; +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class SqlServerCDCDataSourceFactory implements DataSourceFactory { + + @Override + public String factoryIdentifier() { + return SqlServerCDCDataSourceConfig.PLUGIN_NAME; + } + + @Override + public Set supportedDataSources() { + return Collections.singleton( + SqlServerCDCDataSourceConfig.SQLSERVER_CDC_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new SqlServerCDCDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCOptionRule.java new file mode 100644 index 000000000..95cf360b0 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCOptionRule.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.sqlserver; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +public class SqlServerCDCOptionRule { + + public static final Option BASE_URL = + Options.key("base-url") + .stringType() + .noDefaultValue() + .withDescription( + "URL has to be without database, like \"jdbc:sqlserver://localhost:1433;databaseName=test/\""); + + public static final Option USERNAME = + Options.key("username") + .stringType() + .noDefaultValue() + .withDescription( + "Name of the database to use when connecting to the database server."); + + public static final Option PASSWORD = + Options.key("password") + .stringType() + .noDefaultValue() + .withDescription("Password to use when connecting to the database server."); + + public static final Option DATABASE_NAME = + Options.key("database-name") + .stringType() + .noDefaultValue() + .withDescription("Database name of the database to monitor."); + + public static final Option TABLE_NAME = + Options.key("table-name") + .stringType() + .noDefaultValue() + .withDescription("Table name of the database to monitor."); + public static final Option SERVER_TIME_ZONE = + Options.key("server-time-zone") + .stringType() + .defaultValue("UTC") + .withDescription("The session time zone in database server."); + + public static OptionRule optionRule() { + return OptionRule.builder() + .required(USERNAME, PASSWORD, BASE_URL) + .optional(SERVER_TIME_ZONE) + .build(); + } + + public static OptionRule metadataRule() { + return OptionRule.builder().required(DATABASE_NAME, TABLE_NAME).build(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java new file mode 100644 index 000000000..b815a3187 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.cdc.sqlserver.test; + +import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.seatunnel.datasource.plugin.cdc.sqlserver.SqlServerCDCDataSourceChannel; +import org.apache.seatunnel.datasource.plugin.cdc.sqlserver.SqlServerCDCDataSourceConfig; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class TestSqlServerCDCDataSourceChannel { + + @Test + @Disabled + public void testConnect() { + SqlServerCDCDataSourceChannel channel = new SqlServerCDCDataSourceChannel(); + Map requestParams = new TreeMap<>(); + requestParams.put("base-url", "jdbc:sqlserver://localhost:1433;databaseName=test"); + requestParams.put("username", "sa"); + requestParams.put("password", "MyPass@word"); + + for (String database : channel.getDatabases(SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams)) { + final List tables = channel.getTables(SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams, database); + final Map> tableFields = + channel.getTableFields(SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams, database, tables); + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/pom.xml new file mode 100644 index 000000000..c25a44b30 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/pom.xml @@ -0,0 +1,57 @@ + + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource-plugins + ${revision} + + + datasource-starrocks + + + 8.0.16 + + + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + + com.google.auto.service + auto-service + + + org.apache.seatunnel + seatunnel-api + + + mysql + mysql-connector-java + ${mysql.version} + + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java new file mode 100644 index 000000000..c04558b07 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.apache.seatunnel.api.table.catalog.PrimaryKey; +import org.apache.seatunnel.api.table.catalog.TablePath; +import org.apache.seatunnel.api.table.catalog.exception.CatalogException; +import org.apache.seatunnel.api.table.catalog.exception.DatabaseNotExistException; +import org.apache.seatunnel.api.table.catalog.exception.TableNotExistException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public class StarRocksCatalog { + + protected final String catalogName; + protected final String username; + protected final String pwd; + protected final String baseUrl; + protected final String defaultUrl; + + private static final Set SYS_DATABASES = new HashSet<>(); + private static final Logger LOG = LoggerFactory.getLogger(StarRocksCatalog.class); + + static { + SYS_DATABASES.add("information_schema"); + SYS_DATABASES.add("_statistics_"); + } + + public StarRocksCatalog(String catalogName, String username, String pwd, String defaultUrl) { + + checkArgument(StringUtils.isNotBlank(username)); + checkArgument(StringUtils.isNotBlank(pwd)); + checkArgument(StringUtils.isNotBlank(defaultUrl)); + + defaultUrl = defaultUrl.trim(); + this.catalogName = catalogName; + this.username = username; + this.pwd = pwd; + this.defaultUrl = defaultUrl; + if (validateJdbcUrlWithDatabase(defaultUrl)) { + this.baseUrl = splitDefaultUrl(defaultUrl); + } else { + this.baseUrl = defaultUrl.endsWith("/") ? defaultUrl : defaultUrl + "/"; + } + } + + public List listDatabases() throws CatalogException { + List databases = new ArrayList<>(); + try (Connection conn = DriverManager.getConnection(defaultUrl, username, pwd); + PreparedStatement ps = conn.prepareStatement("SHOW DATABASES;"); + ResultSet rs = ps.executeQuery();) { + + while (rs.next()) { + String databaseName = rs.getString(1); + if (!SYS_DATABASES.contains(databaseName)) { + databases.add(rs.getString(1)); + } + } + + return databases; + } catch (Exception e) { + throw new CatalogException( + String.format("Failed listing database in catalog %s", this.catalogName), e); + } + } + + public List listTables(String databaseName) + throws CatalogException, DatabaseNotExistException { + if (!databaseExists(databaseName)) { + throw new DatabaseNotExistException(this.catalogName, databaseName); + } + + try (Connection conn = DriverManager.getConnection(baseUrl + databaseName, username, pwd); + PreparedStatement ps = conn.prepareStatement("SHOW TABLES;"); + ResultSet rs = ps.executeQuery()) { + + List tables = new ArrayList<>(); + + while (rs.next()) { + tables.add(rs.getString(1)); + } + + return tables; + } catch (Exception e) { + throw new CatalogException( + String.format("Failed listing database in catalog %s", catalogName), e); + } + } + + public List getTable(TablePath tablePath) + throws CatalogException, TableNotExistException { + if (!tableExists(tablePath)) { + throw new TableNotExistException(catalogName, tablePath); + } + + String dbUrl = baseUrl + tablePath.getDatabaseName(); + try (Connection conn = DriverManager.getConnection(dbUrl, username, pwd); + PreparedStatement statement = + conn.prepareStatement( + String.format( + "SELECT * FROM %s WHERE 1 = 0;", + String.format( + "`%s`.`%s`", + tablePath.getDatabaseName(), + tablePath.getTableName())));) { + + Optional primaryKey = + getPrimaryKey(tablePath.getDatabaseName(), tablePath.getTableName()); + + ResultSetMetaData tableMetaData = statement.getMetaData(); + + List fields = new ArrayList<>(); + for (int i = 1; i <= tableMetaData.getColumnCount(); i++) { + TableField tableField = new TableField(); + tableField.setName(tableMetaData.getColumnName(i)); + tableField.setType(tableMetaData.getColumnTypeName(i)); + tableField.setComment(tableMetaData.getColumnLabel(i)); + tableField.setNullable( + tableMetaData.isNullable(i) == ResultSetMetaData.columnNullable); + tableField.setPrimaryKey( + primaryKey.isPresent() + && primaryKey + .get() + .getColumnNames() + .contains(tableField.getName())); + // TODO add default value + tableField.setDefaultValue(null); + fields.add(tableField); + } + return fields; + } catch (Exception e) { + throw new CatalogException( + String.format("Failed getting table %s", tablePath.getFullName()), e); + } + } + + /** + * @return The array size is fixed at 2, index 0 is base url, and index 1 is default database. + */ + public static String splitDefaultUrl(String defaultUrl) { + int index = defaultUrl.lastIndexOf("/") + 1; + return defaultUrl.substring(0, index); + } + + protected Optional getPrimaryKey(String schema, String table) throws SQLException { + + List pkFields = new ArrayList<>(); + try (Connection conn = DriverManager.getConnection(defaultUrl, username, pwd); + PreparedStatement statement = + conn.prepareStatement( + String.format( + "SELECT COLUMN_NAME FROM information_schema.columns where TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s' AND COLUMN_KEY = 'PRI' ORDER BY ORDINAL_POSITION", + schema, table)); + ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + String columnName = rs.getString("COLUMN_NAME"); + pkFields.add(columnName); + } + } + if (!pkFields.isEmpty()) { + // PK_NAME maybe null according to the javadoc, generate a unique name in that case + String pkName = "pk_" + String.join("_", pkFields); + return Optional.of(PrimaryKey.of(pkName, pkFields)); + } + return Optional.empty(); + } + + public boolean databaseExists(String databaseName) throws CatalogException { + checkArgument(StringUtils.isNotBlank(databaseName)); + + return listDatabases().contains(databaseName); + } + + /** + * URL has to be with database, like "jdbc:mysql://localhost:5432/db" rather than + * "jdbc:mysql://localhost:5432/". + */ + @SuppressWarnings("MagicNumber") + public static boolean validateJdbcUrlWithDatabase(String url) { + String[] parts = url.trim().split("\\/+"); + return parts.length == 3; + } + + public boolean tableExists(TablePath tablePath) throws CatalogException { + try { + return databaseExists(tablePath.getDatabaseName()) + && listTables(tablePath.getDatabaseName()).contains(tablePath.getTableName()); + } catch (DatabaseNotExistException e) { + return false; + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java new file mode 100644 index 000000000..a5ed265f4 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.api.table.catalog.TablePath; +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; +import org.apache.seatunnel.datasource.plugin.api.model.TableField; + +import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class StarRocksDataSourceChannel implements DataSourceChannel { + + private static final Logger LOGGER = LoggerFactory.getLogger(StarRocksDataSourceChannel.class); + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Override + public boolean canAbleGetSchema() { + return true; + } + + @Override + public OptionRule getDataSourceOptions(@NonNull String pluginName) { + return StarRocksOptionRule.optionRule(); + } + + @Override + public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) { + return StarRocksOptionRule.metadataRule(); + } + + @Override + public List getTables( + @NonNull String pluginName, Map requestParams, String database) { + StarRocksCatalog catalog = getCatalog(requestParams); + return catalog.listTables(database); + } + + @Override + public List getDatabases( + @NonNull String pluginName, @NonNull Map requestParams) { + StarRocksCatalog catalog = getCatalog(requestParams); + return catalog.listDatabases(); + } + + @Override + public boolean checkDataSourceConnectivity( + @NonNull String pluginName, @NonNull Map requestParams) { + try { + StarRocksCatalog catalog = getCatalog(requestParams); + String nodeUrls = requestParams.get(StarRocksOptionRule.NODE_URLS.key()); + List nodeList = OBJECT_MAPPER.readValue(nodeUrls, List.class); + if (!telnet(nodeList.get(0))) { + return false; + } + catalog.listDatabases(); + return true; + } catch (Exception e) { + throw new DataSourcePluginException( + "check StarRocks connectivity failed, " + e.getMessage(), e); + } + } + + @SuppressWarnings("checkstyle:MagicNumber") + private static boolean telnet(String nodeUrl) throws IOException { + Socket socket = new Socket(); + boolean isConnected; + try { + String[] hostAndPort = nodeUrl.split(":"); + socket.connect( + new InetSocketAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1])), 1000); + isConnected = socket.isConnected(); + } catch (IOException e) { + LOGGER.error("telnet error", e); + throw e; + } finally { + try { + socket.close(); + } catch (IOException e) { + LOGGER.error("Release Socket Connection Error", e); + } + } + return isConnected; + } + + @Override + public List getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { + StarRocksCatalog catalog = getCatalog(requestParams); + return catalog.getTable(TablePath.of(database, table)); + } + + @Override + public Map> getTableFields( + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { + StarRocksCatalog catalog = getCatalog(requestParams); + Map> tableFields = new HashMap<>(); + tables.forEach( + table -> tableFields.put(table, catalog.getTable(TablePath.of(database, table)))); + return tableFields; + } + + private StarRocksCatalog getCatalog(Map requestParams) { + try { + String username = requestParams.get(StarRocksOptionRule.USERNAME.key()); + String password = requestParams.get(StarRocksOptionRule.PASSWORD.key()); + String jdbc = requestParams.get(StarRocksOptionRule.BASE_URL.key()); + return new StarRocksCatalog("StarRocks", username, password, jdbc); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceConfig.java new file mode 100644 index 000000000..3c4063a2c --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceConfig.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks; + +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; +import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum; + +public class StarRocksDataSourceConfig { + + public static final String PLUGIN_NAME = "StarRocks"; + + public static final DataSourcePluginInfo STARROCKS_DATASOURCE_PLUGIN_INFO = + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceFactory.java new file mode 100644 index 000000000..f4ecddec5 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceFactory.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks; + +import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; +import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory; +import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo; + +import com.google.auto.service.AutoService; + +import java.util.Collections; +import java.util.Set; + +@AutoService(DataSourceFactory.class) +public class StarRocksDataSourceFactory implements DataSourceFactory { + @Override + public String factoryIdentifier() { + return "StarRocks"; + } + + @Override + public Set supportedDataSources() { + return Collections.singleton(StarRocksDataSourceConfig.STARROCKS_DATASOURCE_PLUGIN_INFO); + } + + @Override + public DataSourceChannel createChannel() { + return new StarRocksDataSourceChannel(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksOptionRule.java new file mode 100644 index 000000000..6267e1556 --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksOptionRule.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.datasource.plugin.starrocks; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.Options; +import org.apache.seatunnel.api.configuration.util.OptionRule; + +import java.util.List; + +public class StarRocksOptionRule { + + public static final Option> NODE_URLS = + Options.key("nodeUrls") + .listType() + .noDefaultValue() + .withDescription( + "StarRocks cluster address, the format is [\"fe_ip:fe_http_port\", ...]"); + + public static final Option USERNAME = + Options.key("username") + .stringType() + .noDefaultValue() + .withDescription("StarRocks user username"); + + public static final Option PASSWORD = + Options.key("password") + .stringType() + .noDefaultValue() + .withDescription("StarRocks user password"); + + public static final Option DATABASE = + Options.key("database") + .stringType() + .noDefaultValue() + .withDescription("The name of StarRocks database"); + + public static final Option TABLE = + Options.key("table") + .stringType() + .noDefaultValue() + .withDescription("The name of StarRocks table"); + + public static final Option BASE_URL = + Options.key("base-url") + .stringType() + .noDefaultValue() + .withDescription( + "URL has to be without database, like \"jdbc:mysql://localhost:5432/\" or" + + "\"jdbc:mysql://localhost:5432\" rather than \"jdbc:mysql://localhost:5432/db\""); + + public static OptionRule optionRule() { + return OptionRule.builder().required(NODE_URLS, USERNAME, PASSWORD, BASE_URL).build(); + } + + public static OptionRule metadataRule() { + return OptionRule.builder().required(DATABASE, TABLE).build(); + } +} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml new file mode 100644 index 000000000..039df6b4e --- /dev/null +++ b/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + org.apache.seatunnel + seatunnel-datasource + ${revision} + + + seatunnel-datasource-plugins + pom + + + datasource-plugins-api + datasource-all + datasource-kafka + datasource-elasticsearch + datasource-jdbc-hive + datasource-s3-redshift + datasource-starrocks + datasource-jdbc-clickhouse + datasource-jdbc-mysql + datasource-jdbc-oracle + datasource-jdbc-postgresql + datasource-jdbc-redshift + datasource-jdbc-sqlserver + datasource-jdbc-starrocks + datasource-mysql-cdc + datasource-s3 + datasource-sqlserver-cdc + datasource-jdbc-tidb + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + ${e2e.dependency.skip} + true + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + + diff --git a/seatunnel-server/pom.xml b/seatunnel-server/pom.xml index a71c78e3c..7f1e1defa 100644 --- a/seatunnel-server/pom.xml +++ b/seatunnel-server/pom.xml @@ -70,6 +70,11 @@ spring-boot-starter-jdbc ${spring-boot.version} + + com.google.code.gson + gson + 2.8.6 + com.alibaba druid-spring-boot-starter diff --git a/seatunnel-server/seatunnel-app/pom.xml b/seatunnel-server/seatunnel-app/pom.xml index ede117405..7458e5895 100644 --- a/seatunnel-server/seatunnel-app/pom.xml +++ b/seatunnel-server/seatunnel-app/pom.xml @@ -25,16 +25,26 @@ seatunnel-app - - 1.8 - seatunnel-ui - - org.apache.seatunnel seatunnel-common + + org.apache.seatunnel + seatunnel-dynamicform + ${project.version} + + + org.apache.seatunnel + seatunnel-scheduler-dolphinscheduler + ${project.version} + + + org.apache.seatunnel + seatunnel-server-common + ${project.version} + org.springframework.boot @@ -76,6 +86,11 @@ mybatis-plus-boot-starter + + com.google.code.gson + gson + + io.springfox springfox-swagger2 @@ -194,129 +209,12 @@ com.cronutils cron-utils ${cron-utils.version} + + + javassist + org.javassist + + - - - - - org.apache.maven.plugins - maven-jar-plugin - - - *.yml - *.yaml - *.xml - - - - - - com.github.eirslett - frontend-maven-plugin - 1.11.3 - - ${project.basedir}/../../${frontend.project.name} - - - - install node and npm - - install-node-and-npm - - - v16.16.0 - 8.19.2 - - - - install - - npm - - generate-resources - - install --ignore-scripts - - - - build - - npm - - - run build:prod - - - - - - - - - release - - - release - true - - - - apache-seatunnel-web-incubating-${project.version} - - - maven-assembly-plugin - - - seatunnel-web - - single - - package - - - src/main/assembly/seatunnel-web.xml - - false - - - - - - - - - ci - - true - - release - true - - - - apache-seatunnel-web-incubating-${project.version} - - - maven-assembly-plugin - - - seatunnel-web - - single - - package - - - src/main/assembly/seatunnel-web-ci.xml - - false - - - - - - - - \ No newline at end of file diff --git a/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml b/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml deleted file mode 100644 index 5dce8b831..000000000 --- a/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - seatunnel-web - - dir - zip - - false - - - ${basedir}/src/main/resources - - *.yml - *.yaml - *.xml - - conf - - - ${basedir}/src/main/resources/script - - *.* - - script - - - ${basedir}/src/main/bin - - *.sh - - bin - - - ${basedir}/../../seatunnel-ui/dist - - **/*.* - - ui - - - - - libs - - - diff --git a/seatunnel-server/seatunnel-app/src/main/bin/seatunnel-backend-daemon.sh b/seatunnel-server/seatunnel-app/src/main/bin/seatunnel-backend-daemon.sh index 285f39cd1..005b2faac 100644 --- a/seatunnel-server/seatunnel-app/src/main/bin/seatunnel-backend-daemon.sh +++ b/seatunnel-server/seatunnel-app/src/main/bin/seatunnel-backend-daemon.sh @@ -37,7 +37,7 @@ start() { echo "$WORKDIR" $JAVA_HOME/bin/java $JAVA_OPTS \ - -cp "$WORKDIR/../conf":"$WORKDIR/../libs/*" \ + -cp "$WORKDIR/../conf":"$WORKDIR/../libs/*":"$WORKDIR/../datasource/*" \ $SPRING_OPTS \ org.apache.seatunnel.app.SeatunnelApplication echo "seatunnel started" diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java index b2d99503f..39ab914ca 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java @@ -44,7 +44,7 @@ public class AuthController { @ApiImplicitParam(name = "username", value = "user name", dataType = "String"), @ApiImplicitParam(name = "roleName", value = "role name", dataType = "String"), }) - public Result userRole(@RequestParam("username") @NotNull String username, @RequestParam("roleName") @NotNull String roleName){ + public Result userRole(@RequestParam("username") @NotNull String username, @RequestParam("roleName") @NotNull String roleName) { final boolean b = roleServiceImpl.checkUserRole(username, roleName); return Result.success(b); } diff --git a/seatunnel-server/seatunnel-app/src/main/resources/application.yml b/seatunnel-server/seatunnel-app/src/main/resources/application.yml index a873c088b..bf297a4a2 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/application.yml +++ b/seatunnel-server/seatunnel-app/src/main/resources/application.yml @@ -39,7 +39,7 @@ ds: tenant: default: gaojun api: - token: 296fecc6e5f78e6a5aa39106707476fe + token: fcec21193f5e00189aad3fef7bc50c6e prefix: http://localhost:12345/dolphinscheduler jwt: diff --git a/seatunnel-web-dist/pom.xml b/seatunnel-web-dist/pom.xml index 4a8402981..7e8aed763 100644 --- a/seatunnel-web-dist/pom.xml +++ b/seatunnel-web-dist/pom.xml @@ -30,33 +30,360 @@ true + seatunnel-ui - seatunnel-dist + seatunnel-web-dist pom - maven-assembly-plugin + com.github.eirslett + frontend-maven-plugin + 1.11.3 + + ${project.basedir}/../${frontend.project.name} + - src - package + install node and npm - single + install-node-and-npm - - src/main/assembly/assembly-src.xml - - true + v16.16.0 + 8.19.2 + + + + install + + npm + + generate-resources + + install --ignore-scripts + + + + build + + npm + + + run build:prod - - apache-seatunnel-web-incubating-${project.version} + + + release + + + release + true + + + + + + org.apache.seatunnel + seatunnel-app + ${project.version} + provided + + + org.apache.seatunnel + seatunnel-datasource-client + ${project.version} + provided + + + + + apache-seatunnel-web-incubating-${project.version} + + + maven-assembly-plugin + + + seatunnel-web + + single + + package + + + src/main/assembly/seatunnel-web.xml + + false + + + + + + + + + ci + + true + + release + true + + + + + + org.apache.seatunnel + seatunnel-app + ${project.version} + provided + + + org.apache.seatunnel + seatunnel-datasource-client + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-clickhouse + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-hive + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-mysql + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-oracle + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-postgresql + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-tidb + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-redshift + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-sqlserver + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-jdbc-starrocks + ${project.version} + provided + + + * + * + + + + + + org.apache.seatunnel + datasource-kafka + ${project.version} + provided + + + * + * + + + + + + org.apache.seatunnel + datasource-elasticsearch + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-s3-redshift + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-starrocks + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-mysql-cdc + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-s3 + ${project.version} + provided + + + * + * + + + + + org.apache.seatunnel + datasource-sqlserver-cdc + ${project.version} + provided + + + * + * + + + + + + + mysql + mysql-connector-java + ${mysql.version} + provided + + + + + apache-seatunnel-web-incubating-${project.version} + + + maven-assembly-plugin + + + seatunnel-web + + single + + package + + + src/main/assembly/seatunnel-web-ci.xml + + false + + + + + + + + diff --git a/seatunnel-web-dist/release-docs/binary/LICENSE b/seatunnel-web-dist/release-docs/LICENSE similarity index 96% rename from seatunnel-web-dist/release-docs/binary/LICENSE rename to seatunnel-web-dist/release-docs/LICENSE index ece66a187..ad7f6d8cf 100644 --- a/seatunnel-web-dist/release-docs/binary/LICENSE +++ b/seatunnel-web-dist/release-docs/LICENSE @@ -221,7 +221,7 @@ The text of each license is the standard Apache 2.0 license. (The Apache Software License, Version 2.0) ClassMate (com.fasterxml:classmate:1.3.1 - http://github.com/cowtowncoder/java-classmate) (Apache License, Version 2.0) Apache Commons Codec (commons-codec:commons-codec:1.13 - https://commons.apache.org/proper/commons-codec/) (Apache License, Version 2.0) Apache Commons Collections (org.apache.commons:commons-collections4:4.4 - https://commons.apache.org/proper/commons-collections/) - (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.4 - http://commons.apache.org/proper/commons-lang/) + (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.12.0 - http://commons.apache.org/proper/commons-lang/) (Apache License, Version 2.0) config (com.typesafe:config:1.3.3 - https://github.com/lightbend/config) (The Apache Software License, Version 2.0) Guava: Google Core Libraries for Java (com.google.guava:guava:19.0 - https://github.com/google/guava/guava) (Apache License 2.0) Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:6.2.2.Final - http://hibernate.org/validator/hibernate-validator) @@ -290,7 +290,9 @@ The text of each license is the standard Apache 2.0 license. (Apache License 2.0) swagger-models (io.swagger:swagger-models:1.5.10 - https://github.com/swagger-api/swagger-core/modules/swagger-models) (Apache License, Version 2.0) tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:9.0.63 - https://tomcat.apache.org/) (Apache License, Version 2.0) scala-liberary (org.scala-lang:scala-library:jar:2.11.12 - https://www.scala-lang.org/) - + (Apache License, Version 2.0) gson (com.google.code.gson:gson:jar:2.8.6 - https://mvnrepository.com/artifact/com.google.code.gson/gson/2.8.6) + (Apache License, Version 2.0) cron-utils (com.cronutils:cron-utils:jar:9.1.6 - https://mvnrepository.com/artifact/com.cronutils/cron-utils/9.1.6) + ======================================================================== Eclipse Public License @@ -317,6 +319,7 @@ The text of each license is also included at licenses/LICENSE-[project].txt. (CDDL) javax.annotation API (javax.annotation:javax.annotation-api:1.3.2 - http://jcp.org/en/jsr/detail?id=250) + (CDDL and GPL and GPL 2.0) javax.el (org.glassfish:javax.el:3.0.0 https://mvnrepository.com/artifact/org.glassfish/javax.el/3.0.0) ======================================================================== MIT License @@ -328,7 +331,15 @@ The text of each license is also included at licenses/LICENSE-[project].txt. (MIT License) SLF4J API Module (org.slf4j:slf4j-api:1.7.25 - http://www.slf4j.org) (MIT License) SLF4J LOG4J-12 Binding (org.slf4j:slf4j-log4j12:1.7.25 - http://www.slf4j.org) (The MIT License) jsoup Java HTML Parser (org.jsoup:jsoup:1.14.3 - https://jsoup.org/) - (MIT License) JUL to SLF4J bridge (org.slf4j:jul-to-slf4j:1.7.36 - http://www.slf4j.org) + (MIT License) JUL to SLF4J bridge (org.slf4j:jul-to-slf4j:1.7.36 - http://www.slf4j.org) + +======================================================================== +MPL 1.1 licenses +======================================================================== +The following components are provided under a MPL 1.1 license. See project link for details. +The text of each license is also included at licenses/LICENSE-[project].txt. + + (MPL-1.1) javassist (org.javassist:javassist:3.27.0-GA - https://github.com/jboss-javassist/javassist) \ No newline at end of file diff --git a/seatunnel-web-dist/release-docs/binary/NOTICE b/seatunnel-web-dist/release-docs/NOTICE similarity index 100% rename from seatunnel-web-dist/release-docs/binary/NOTICE rename to seatunnel-web-dist/release-docs/NOTICE diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-Jetty-9.x.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-Jetty-9.x.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-Jetty-9.x.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-Jetty-9.x.txt diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-aspectj.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-aspectj.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-aspectj.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-aspectj.txt diff --git a/seatunnel-web-dist/release-docs/licenses/LICENSE-cron-utils.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-cron-utils.txt new file mode 100644 index 000000000..7a4a3ea24 --- /dev/null +++ b/seatunnel-web-dist/release-docs/licenses/LICENSE-cron-utils.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-h2.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-h2.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-h2.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-h2.txt diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-jakarta.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-jakarta.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-jakarta.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-jakarta.txt diff --git a/seatunnel-web-dist/release-docs/licenses/LICENSE-javassist.html b/seatunnel-web-dist/release-docs/licenses/LICENSE-javassist.html new file mode 100644 index 000000000..66bcfb885 --- /dev/null +++ b/seatunnel-web-dist/release-docs/licenses/LICENSE-javassist.html @@ -0,0 +1,373 @@ + + +Javassist License + + + + +

MOZILLA PUBLIC LICENSE
Version +1.1 +

+


+
+

1. Definitions. +

    1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. +

    1.1. ''Contributor'' means each entity that creates or contributes + to the creation of Modifications. +

    1.2. ''Contributor Version'' means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications made by + that particular Contributor. +

    1.3. ''Covered Code'' means the Original Code or Modifications or + the combination of the Original Code and Modifications, in each case including + portions thereof. +

    1.4. ''Electronic Distribution Mechanism'' means a mechanism + generally accepted in the software development community for the electronic + transfer of data. +

    1.5. ''Executable'' means Covered Code in any form other than Source + Code. +

    1.6. ''Initial Developer'' means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. +

    1.7. ''Larger Work'' means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. +

    1.8. ''License'' means this document. +

    1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or subsequently + acquired, any and all of the rights conveyed herein. +

    1.9. ''Modifications'' means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: +

      A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. +

      B. Any new file that contains any part of the Original Code or + previous Modifications.
       

    1.10. ''Original Code'' + means Source Code of computer software code which is described in the Source + Code notice required by Exhibit A as Original Code, and which, at the + time of its release under this License is not already Covered Code governed by + this License. +

    1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation,  method, process, and + apparatus claims, in any patent Licensable by grantor. +

    1.11. ''Source Code'' means the preferred form of the Covered Code + for making modifications to it, including all modules it contains, plus any + associated interface definition files, scripts used to control compilation and + installation of an Executable, or source code differential comparisons against + either the Original Code or another well known, available Covered Code of the + Contributor's choice. The Source Code can be in a compressed or archival form, + provided the appropriate decompression or de-archiving software is widely + available for no charge. +

    1.12. "You'' (or "Your")  means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this License + or a future version of this License issued under Section 6.1. For legal + entities, "You'' includes any entity which controls, is controlled by, or is + under common control with You. For purposes of this definition, "control'' + means (a) the power, direct or indirect, to cause the direction or management + of such entity, whether by contract or otherwise, or (b) ownership of more + than fifty percent (50%) of the outstanding shares or beneficial ownership of + such entity.

2. Source Code License. +
    2.1. The Initial Developer Grant.
    The Initial Developer hereby + grants You a world-wide, royalty-free, non-exclusive license, subject to third + party intellectual property claims: +
      (a)  under intellectual property rights (other than + patent or trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original Code (or + portions thereof) with or without Modifications, and/or as part of a Larger + Work; and +

      (b) under Patents Claims infringed by the making, using or selling + of Original Code, to make, have made, use, practice, sell, and offer for + sale, and/or otherwise dispose of the Original Code (or portions thereof). +

        +
        (c) the licenses granted in this Section 2.1(a) and (b) + are effective on the date Initial Developer first distributes Original Code + under the terms of this License. +

        (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) separate + from the Original Code;  or 3) for infringements caused by: i) the + modification of the Original Code or ii) the combination of the Original + Code with other software or devices.
         

      2.2. Contributor + Grant.
      Subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, non-exclusive + license +

        (a)  under intellectual property rights (other + than patent or trademark) Licensable by Contributor, to use, reproduce, + modify, display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an unmodified + basis, with other Modifications, as Covered Code and/or as part of a Larger + Work; and +

        (b) under Patent Claims infringed by the making, using, or selling + of  Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: 1) Modifications made by that Contributor (or portions + thereof); and 2) the combination of  Modifications made by that + Contributor with its Contributor Version (or portions of such + combination). +

        (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of the Covered + Code. +

        (d)    Notwithstanding Section 2.2(b) above, no + patent license is granted: 1) for any code that Contributor has deleted from + the Contributor Version; 2)  separate from the Contributor + Version;  3)  for infringements caused by: i) third party + modifications of Contributor Version or ii)  the combination of + Modifications made by that Contributor with other software  (except as + part of the Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by that + Contributor.

    +


    3. Distribution Obligations. +

      3.1. Application of License.
      The Modifications which You create + or to which You contribute are governed by the terms of this License, + including without limitation Section 2.2. The Source Code version of + Covered Code may be distributed only under the terms of this License or a + future version of this License released under Section 6.1, and You must + include a copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code version + that alters or restricts the applicable version of this License or the + recipients' rights hereunder. However, You may include an additional document + offering the additional rights described in Section 3.5. +

      3.2. Availability of Source Code.
      Any Modification which You + create or to which You contribute must be made available in Source Code form + under the terms of this License either on the same media as an Executable + version or via an accepted Electronic Distribution Mechanism to anyone to whom + you made an Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) months + after the date it initially became available, or at least six (6) months after + a subsequent version of that particular Modification has been made available + to such recipients. You are responsible for ensuring that the Source Code + version remains available even if the Electronic Distribution Mechanism is + maintained by a third party. +

      3.3. Description of Modifications.
      You must cause all Covered + Code to which You contribute to contain a file documenting the changes You + made to create that Covered Code and the date of any change. You must include + a prominent statement that the Modification is derived, directly or + indirectly, from Original Code provided by the Initial Developer and including + the name of the Initial Developer in (a) the Source Code, and (b) in any + notice in an Executable version or related documentation in which You describe + the origin or ownership of the Covered Code. +

      3.4. Intellectual Property Matters +

        (a) Third Party Claims.
        If Contributor has knowledge that a + license under a third party's intellectual property rights is required to + exercise the rights granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code distribution + titled "LEGAL'' which describes the claim and the party making the claim in + sufficient detail that a recipient will know whom to contact. If Contributor + obtains such knowledge after the Modification is made available as described + in Section 3.2, Contributor shall promptly modify the LEGAL file in all + copies Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) reasonably + calculated to inform those who received the Covered Code that new knowledge + has been obtained. +

        (b) Contributor APIs.
        If Contributor's Modifications include + an application programming interface and Contributor has knowledge of patent + licenses which are reasonably necessary to implement that API, Contributor + must also include this information in the LEGAL file. +
         

                + (c)    Representations. +
        Contributor represents that, except as disclosed pursuant to Section + 3.4(a) above, Contributor believes that Contributor's Modifications are + Contributor's original creation(s) and/or Contributor has sufficient rights + to grant the rights conveyed by this License.
      +


      3.5. Required Notices.
      You must duplicate the notice in + Exhibit A in each file of the Source Code.  If it is not possible + to put such notice in a particular Source Code file due to its structure, then + You must include such notice in a location (such as a relevant directory) + where a user would be likely to look for such a notice.  If You created + one or more Modification(s) You may add your name as a Contributor to the + notice described in Exhibit A.  You must also duplicate this + License in any documentation for the Source Code where You describe + recipients' rights or ownership rights relating to Covered Code.  You may + choose to offer, and to charge a fee for, warranty, support, indemnity or + liability obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial Developer + or any Contributor. You must make it absolutely clear than any such warranty, + support, indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every Contributor for any + liability incurred by the Initial Developer or such Contributor as a result of + warranty, support, indemnity or liability terms You offer. +

      3.6. Distribution of Executable Versions.
      You may distribute + Covered Code in Executable form only if the requirements of Section + 3.1-3.5 have been met for that Covered Code, and if You include a + notice stating that the Source Code version of the Covered Code is available + under the terms of this License, including a description of how and where You + have fulfilled the obligations of Section 3.2. The notice must be + conspicuously included in any notice in an Executable version, related + documentation or collateral in which You describe recipients' rights relating + to the Covered Code. You may distribute the Executable version of Covered Code + or ownership rights under a license of Your choice, which may contain terms + different from this License, provided that You are in compliance with the + terms of this License and that the license for the Executable version does not + attempt to limit or alter the recipient's rights in the Source Code version + from the rights set forth in this License. If You distribute the Executable + version under a different license You must make it absolutely clear that any + terms which differ from this License are offered by You alone, not by the + Initial Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of any such terms You offer. + +

      3.7. Larger Works.
      You may create a Larger Work by combining + Covered Code with other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, You must make + sure the requirements of this License are fulfilled for the Covered +Code.

    4. Inability to Comply Due to Statute or Regulation. +
      If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to statute, + judicial order, or regulation then You must: (a) comply with the terms of this + License to the maximum extent possible; and (b) describe the limitations and + the code they affect. Such description must be included in the LEGAL file + described in Section 3.4 and must be included with all distributions of + the Source Code. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it.
    5. Application of this License. +
      This License applies to code to which the Initial Developer has attached + the notice in Exhibit A and to related Covered Code.
    6. Versions +of the License. +
      6.1. New Versions.
      Netscape Communications Corporation + (''Netscape'') may publish revised and/or new versions of the License from + time to time. Each version will be given a distinguishing version number. +

      6.2. Effect of New Versions.
      Once Covered Code has been + published under a particular version of the License, You may always continue + to use it under the terms of that version. You may also choose to use such + Covered Code under the terms of any subsequent version of the License + published by Netscape. No one other than Netscape has the right to modify the + terms applicable to Covered Code created under this License. +

      6.3. Derivative Works.
      If You create or use a modified version + of this License (which you may only do in order to apply it to code which is + not already Covered Code governed by this License), You must (a) rename Your + license so that the phrases ''Mozilla'', ''MOZILLAPL'', ''MOZPL'', + ''Netscape'', "MPL", ''NPL'' or any confusingly similar phrase do not appear + in your license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license contains + terms which differ from the Mozilla Public License and Netscape Public + License. (Filling in the name of the Initial Developer, Original Code or + Contributor in the notice described in Exhibit A shall not of + themselves be deemed to be modifications of this License.)

    7. +DISCLAIMER OF WARRANTY. +
      COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS'' BASIS, WITHOUT + WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT + LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, + FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE + QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED + CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY + OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR + CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS + LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER.
    8. TERMINATION. +
      8.1.  This License and the rights granted hereunder will + terminate automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall survive any + termination of this License. Provisions which, by their nature, must remain in + effect beyond the termination of this License shall survive. +

      8.2.  If You initiate litigation by asserting a patent + infringement claim (excluding declatory judgment actions) against Initial + Developer or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant")  alleging that: +

      (a)  such Participant's Contributor Version directly or + indirectly infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon + 60 days notice from Participant terminate prospectively, unless if within 60 + days after receipt of notice You either: (i)  agree in writing to pay + Participant a mutually agreeable reasonable royalty for Your past and future + use of Modifications made by such Participant, or (ii) withdraw Your + litigation claim with respect to the Contributor Version against such + Participant.  If within 60 days of notice, a reasonable royalty and + payment arrangement are not mutually agreed upon in writing by the parties or + the litigation claim is not withdrawn, the rights granted by Participant to + You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. +

      (b)  any software, hardware, or device, other than such + Participant's Contributor Version, directly or indirectly infringes any + patent, then any rights granted to You by such Participant under Sections + 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that Participant. +

      8.3.  If You assert a patent infringement claim against + Participant alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as by + license or settlement) prior to the initiation of patent infringement + litigation, then the reasonable value of the licenses granted by such + Participant under Sections 2.1 or 2.2 shall be taken into account in + determining the amount or value of any payment or license. +

      8.4.  In the event of termination under Sections 8.1 or 8.2 + above,  all end user license agreements (excluding distributors and + resellers) which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination.

    9. LIMITATION OF +LIABILITY. +
      UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING + NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY + OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY + OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, + INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR + MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH + PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS + LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR + LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND + LIMITATION MAY NOT APPLY TO YOU.
    10. U.S. GOVERNMENT END USERS. +
      The Covered Code is a ''commercial item,'' as that term is defined in 48 + C.F.R. 2.101 (Oct. 1995), consisting of ''commercial computer software'' and + ''commercial computer software documentation,'' as such terms are used in 48 + C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. + 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users + acquire Covered Code with only those rights set forth herein.
    11. +MISCELLANEOUS. +
      This License represents the complete agreement concerning subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. This License shall be governed by California law provisions + (except to the extent applicable law, if any, provides otherwise), excluding + its conflict-of-law provisions. With respect to disputes in which at least one + party is a citizen of, or an entity chartered or registered to do business in + the United States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern District of + California, with venue lying in Santa Clara County, California, with the + losing party responsible for costs, including without limitation, court costs + and reasonable attorneys' fees and expenses. The application of the United + Nations Convention on Contracts for the International Sale of Goods is + expressly excluded. Any law or regulation which provides that the language of + a contract shall be construed against the drafter shall not apply to this + License.
    12. RESPONSIBILITY FOR CLAIMS. +
      As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, out of its + utilization of rights under this License and You agree to work with Initial + Developer and Contributors to distribute such responsibility on an equitable + basis. Nothing herein is intended or shall be deemed to constitute any + admission of liability.
    13. MULTIPLE-LICENSED CODE. +
      Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed".  "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under Your + choice of the MPL or the alternative licenses, if any, specified by the + Initial Developer in the file described in Exhibit A.
    +


    EXHIBIT A -Mozilla Public License. +

      The contents of this file are subject to the Mozilla Public License + Version 1.1 (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.mozilla.org/MPL/ +

      Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF
      ANY KIND, either express or implied. See the License + for the specific language governing rights and
      limitations under the + License. +

      The Original Code is Javassist. +

      The Initial Developer of the Original Code is Shigeru Chiba. + Portions created by the Initial Developer are
        + Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. +

      Contributor(s): __Bill Burke, Jason T. Greene______________. + +

      Alternatively, the contents of this software may be used under the +terms of the GNU Lesser General Public License Version 2.1 or later +(the "LGPL"), or the Apache License Version 2.0 (the "AL"), +in which case the provisions of the LGPL or the AL are applicable +instead of those above. If you wish to allow use of your version of +this software only under the terms of either the LGPL or the AL, and not to allow others to +use your version of this software under the terms of the MPL, indicate +your decision by deleting the provisions above and replace them with +the notice and other provisions required by the LGPL or the AL. If you do not +delete the provisions above, a recipient may use your version of this +software under the terms of any one of the MPL, the LGPL or the AL. + +

    + + \ No newline at end of file diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-javax-annotation.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-javax-annotation.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-javax-annotation.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-javax-annotation.txt diff --git a/seatunnel-web-dist/release-docs/licenses/LICENSE-javax.el.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-javax.el.txt new file mode 100644 index 000000000..081ea0ce1 --- /dev/null +++ b/seatunnel-web-dist/release-docs/licenses/LICENSE-javax.el.txt @@ -0,0 +1,759 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and + the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software + or previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available + under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form + of computer software code that is originally released under this + License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, + this License. For legal entities, "You" includes any entity which + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, the Initial Developer + hereby grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Software (or portions thereof), with or without Modifications, + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and + offer for sale, and/or otherwise dispose of the Original Software + (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on + the date Initial Developer first distributes or otherwise makes the + Original Software available to a third party under the terms of this + License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original Software, or + (2) for infringements caused by: (i) the modification of the + Original Software, or (ii) the combination of the Original Software + with other software or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, each Contributor hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof), either on an + unmodified basis, with other Modifications, as Covered Software + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling + of Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective + on the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted from the + Contributor Version; (2) for infringements caused by: (i) third + party modifications of Contributor Version, or (ii) the combination + of Modifications made by that Contributor with other software + (except as part of the Contributor Version) or other devices; or (3) + under Patent Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available + in Executable form must also be made available in Source Code form + and that Source Code form must be distributed only under the terms + of this License. You must include a copy of this License with every + copy of the Source Code form of the Covered Software You distribute + or otherwise make available. You must inform recipients of any such + Covered Software in Executable form as to how they can obtain such + Covered Software in Source Code form in a reasonable manner on or + through a medium customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or You + have sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may not + remove or alter any copyright, patent or trademark notices contained + within the Covered Software, or any notices of licensing or any + descriptive text giving attribution to any Contributor or the + Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version of + this License or the recipients' rights hereunder. You may choose to + offer, and to charge a fee for, warranty, support, indemnity or + liability obligations to one or more recipients of Covered Software. + However, you may do so only on Your own behalf, and not on behalf of + the Initial Developer or any Contributor. You must make it + absolutely clear that any such warranty, support, indemnity or + liability obligation is offered by You alone, and You hereby agree + to indemnify the Initial Developer and every Contributor for any + liability incurred by the Initial Developer or such Contributor as a + result of warranty, support, indemnity or liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under + the terms of this License or under the terms of a license of Your + choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the Covered + Software in Executable form under a different license, You must make + it absolutely clear that any terms which differ from this License + are offered by You alone, not by the Initial Developer or + Contributor. You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial + Developer or such Contributor as a result of any such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and distribute + the Larger Work as a single product. In such a case, You must make + sure the requirements of this License are fulfilled for the Covered + Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or + new versions of this License from time to time. Each version will be + given a distinguishing version number. Except as provided in Section + 4.3, no one other than the license steward has the right to modify + this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. If + the Initial Developer includes a notice in the Original Software + prohibiting it from being distributed or otherwise made available + under any subsequent version of the License, You must distribute and + make the Covered Software available under the terms of the version + of the License under which You originally received the Covered + Software. Otherwise, You may also choose to use, distribute or + otherwise make the Covered Software available under the terms of any + subsequent version of the License published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license and + remove any references to the name of the license steward (except to + note that the license differs from this License); and (b) otherwise + make it clear that the license contains terms which differ from this + License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE + IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR + NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE + DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY + OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, + REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN + ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS + AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond the + termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that the + Participant Software (meaning the Contributor Version where the + Participant is a Contributor or the Original Software where the + Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if the + Initial Developer is not the Participant) and all Contributors under + Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice + from Participant terminate prospectively and automatically at the + expiration of such 60 day notice period, unless if within such 60 + day period You withdraw Your claim with respect to the Participant + Software against such Participant either unilaterally or pursuant to + a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly + infringes any patent where such claim is resolved (such as by + license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE + TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER + FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR + LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE + POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT + APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH + PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH + LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR + LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined + in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. + 252.227-7014(a)(1)) and "commercial computer software documentation" + as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent + with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 + (June 1995), all U.S. Government End Users acquire Covered Software + with only those rights set forth herein. This U.S. Government Rights + clause is in lieu of, and supersedes, any other FAR, DFAR, or other + clause or provision that addresses Government rights in computer + software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + the law of the jurisdiction specified in a notice contained within + the Original Software (except to the extent applicable law, if any, + provides otherwise), excluding such jurisdiction's conflict-of-law + provisions. Any litigation relating to this License shall be subject + to the jurisdiction of the courts located in the jurisdiction and + venue specified in a notice contained within the Original Software, + with the losing party responsible for costs, including, without + limitation, court costs and reasonable attorneys' fees and expenses. + The application of the United Nations Convention on Contracts for + the International Sale of Goods is expressly excluded. Any law or + regulation which provides that the language of a contract shall be + construed against the drafter shall not apply to this License. You + agree that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, distribute + or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +------------------------------------------------------------------------ + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION +LICENSE (CDDL) + +The code released under the CDDL shall be governed by the laws of the +State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction +of the Federal Courts of the Northern District of California and the +state courts of the State of California, with venue lying in Santa Clara +County, California. + + + + The GNU General Public License (GPL) Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor +Boston, MA 02110-1335 +USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you have. +You must make sure that they, too, receive or can get the source code. +And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed under +the terms of this General Public License. The "Program", below, refers +to any such program or work, and a "work based on the Program" means +either the Program or any derivative work under copyright law: that is +to say, a work containing the Program or a portion of it, either +verbatim or with modifications and/or translated into another language. +(Hereinafter, translation is included without limitation in the term +"modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of running +the Program is not restricted, and the output from the Program is +covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy an appropriate copyright notice +and disclaimer of warranty; keep intact all the notices that refer to +this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of +it, thus forming a work based on the Program, and copy and distribute +such modifications or work under the terms of Section 1 above, provided +that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will +not have their licenses terminated so long as such parties remain in +full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot distribute +so as to satisfy simultaneously your obligations under this License and +any other pertinent obligations, then as a consequence you may not +distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you +could satisfy both it and this License would be to refrain entirely from +distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is implemented +by public license practices. Many people have made generous +contributions to the wide range of software distributed through that +system in reliance on consistent application of that system; it is up to +the author/donor to decide if he or she is willing to distribute +software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be +a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License may +add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among countries +not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than `show w' and `show c'; they +could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +# + +Certain source files distributed by Oracle America, Inc. and/or its +affiliates are subject to the following clarification and special +exception to the GPLv2, based on the GNU Project exception for its +Classpath libraries, known as the GNU Classpath Exception, but only +where Oracle has expressly included in the particular source file's +header the words "Oracle designates this particular file as subject to +the "Classpath" exception as provided by Oracle in the LICENSE file +that accompanied this code." + +You should also note that Oracle includes multiple, independent +programs in this software package. Some of those programs are provided +under licenses deemed incompatible with the GPLv2 by the Free Software +Foundation and others. For example, the package includes programs +licensed under the Apache License, Version 2.0. Such programs are +licensed to you under their original licenses. + +Oracle facilitates your further distribution of this package by adding +the Classpath Exception to the necessary parts of its GPLv2 code, which +permits you to use that code in combination with other independent +modules not licensed under the GPLv2. However, note that this would +not permit you to commingle code under an incompatible license with +Oracle's GPLv2 licensed code by, for example, cutting and pasting such +code into a file also containing Oracle's GPLv2 licensed code and then +distributing the result. Additionally, if you were to remove the +Classpath Exception from any of the files to which it applies and +distribute the result, you would likely be required to license some or +all of the other code in that distribution under the GPLv2 as well, and +since the GPLv2 is incompatible with the license terms of some items +included in the distribution by Oracle, removing the Classpath +Exception could therefore effectively compromise your ability to +further distribute the package. + +Proceed with caution and we recommend that you obtain the advice of a +lawyer skilled in open source matters before removing the Classpath +Exception or making modifications to this package which may +subsequently be redistributed and/or involve the use of third party +software. + +CLASSPATH EXCEPTION +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License version 2 cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you may extend this +exception to your version of the library, but you are not obligated to +do so. If you do not wish to do so, delete this exception statement +from your version. \ No newline at end of file diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-jsoup.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-jsoup.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-jsoup.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-jsoup.txt diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-logback.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-logback.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-logback.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-logback.txt diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-mapstruct.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-mapstruct.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-mapstruct.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-mapstruct.txt diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-slf4j.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-slf4j.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-slf4j.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-slf4j.txt diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-spring-plugin.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-spring-plugin.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-spring-plugin.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-spring-plugin.txt diff --git a/seatunnel-web-dist/release-docs/binary/licenses/LICENSE-swagger-core.txt b/seatunnel-web-dist/release-docs/licenses/LICENSE-swagger-core.txt similarity index 100% rename from seatunnel-web-dist/release-docs/binary/licenses/LICENSE-swagger-core.txt rename to seatunnel-web-dist/release-docs/licenses/LICENSE-swagger-core.txt diff --git a/seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml b/seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml new file mode 100644 index 000000000..a13174fcd --- /dev/null +++ b/seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml @@ -0,0 +1,117 @@ + + + + seatunnel-web + + dir + zip + tar.gz + + false + + + ../seatunnel-server/seatunnel-app/src/main/resources + + *.yml + *.yaml + *.xml + + conf + + + ../seatunnel-server/seatunnel-app/src/main/resources/script + + *.* + + script + + + ../seatunnel-server/seatunnel-app/src/main/bin + + *.sh + + bin + + + ../seatunnel-ui/dist + + **/*.* + + ui + + + + release-docs + . + + + + ${basedir}/.././ + + DISCLAIMER + + . + + + + + + false + true + false + + *:*:jar + + + org.apache.seatunnel:datasource-*:jar + + /libs + provided + + + + + false + true + false + /libs + + org.slf4j:slf4j-api:jar + org.slf4j:jcl-over-slf4j:jar + org.apache.logging.log4j:log4j-api:jar + org.apache.logging.log4j:log4j-core:jar + org.apache.logging.log4j:log4j-slf4j-impl:jar + + + + + + false + true + false + + org.apache.seatunnel:datasource-*:jar + + ${artifact.file.name} + /datasource + provided + + + diff --git a/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml b/seatunnel-web-dist/src/main/assembly/seatunnel-web.xml similarity index 59% rename from seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml rename to seatunnel-web-dist/src/main/assembly/seatunnel-web.xml index 91fe5e3c3..5cc991a4d 100644 --- a/seatunnel-server/seatunnel-app/src/main/assembly/seatunnel-web-ci.xml +++ b/seatunnel-web-dist/src/main/assembly/seatunnel-web.xml @@ -27,7 +27,7 @@ false - ${basedir}/src/main/resources + ../seatunnel-server/seatunnel-app/src/main/resources *.yml *.yaml @@ -36,43 +36,69 @@ conf - ${basedir}/src/main/resources/script + ../seatunnel-server/seatunnel-app/src/main/resources/script *.* script - ${basedir}/src/main/bin + ../seatunnel-server/seatunnel-app/src/main/bin *.sh bin - ${basedir}/../../seatunnel-ui/dist + ../seatunnel-ui/dist **/*.* ui + + + release-docs + . + + + + ${basedir}/.././ + + DISCLAIMER + + . + + - libs + false + true + false + + *:*:jar + + + org.apache.seatunnel:datasource-*:jar + + /libs + provided - + + false true false + /libs - mysql:mysql-connector-java:jar - org.postgresql:postgresql:jar + org.slf4j:slf4j-api:jar + org.slf4j:jcl-over-slf4j:jar + org.apache.logging.log4j:log4j-api:jar + org.apache.logging.log4j:log4j-core:jar + org.apache.logging.log4j:log4j-slf4j-impl:jar - ${artifact.file.name} - libs - provided diff --git a/tools/dependencies/checkLicense.sh b/tools/dependencies/checkLicense.sh index e578e5e5f..7de125e95 100755 --- a/tools/dependencies/checkLicense.sh +++ b/tools/dependencies/checkLicense.sh @@ -23,7 +23,7 @@ if [ -d "/tmp/seatunnel-dependencies" ]; then rm -rf /tmp/seatunnel-dependencies/* fi -./mvnw clean --batch-mode --no-snapshot-updates dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=/tmp/seatunnel-dependencies +./mvnw clean -pl '!seatunnel-web-dist' --batch-mode --no-snapshot-updates dependency:copy-dependencies -DincludeScope=runtime -DoutputDirectory=/tmp/seatunnel-dependencies # List all modules(jars) that belong to the SeaTunnel itself, these will be ignored when checking the dependency ls /tmp/seatunnel-dependencies | sort > all-dependencies.txt diff --git a/tools/dependencies/known-dependencies.txt b/tools/dependencies/known-dependencies.txt index 5f7dfbe3b..6b69a0f19 100644 --- a/tools/dependencies/known-dependencies.txt +++ b/tools/dependencies/known-dependencies.txt @@ -3,7 +3,7 @@ aspectjweaver-1.9.7.jar classmate-1.3.1.jar commons-collections4-4.4.jar commons-codec-1.13.jar -commons-lang3-3.4.jar +commons-lang3-3.12.0.jar config-1.3.3.jar cron-utils-9.1.6.jar guava-19.0.jar @@ -18,7 +18,6 @@ jakarta.annotation-api-1.3.5.jar jakarta.servlet-api-4.0.4.jar jakarta.validation-api-2.0.2.jar jakarta.websocket-api-1.1.2.jar -javassist-3.27.0-GA.jar javax.annotation-api-1.3.2.jar javax.el-3.0.0.jar jboss-logging-3.4.1.Final.jar From e0b6e75d45fe6246843d8f50fe9aff77fb30371d Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 11:55:31 +0800 Subject: [PATCH 09/28] Add datasource config switcher (#55) --- .licenserc.yaml | 1 + pom.xml | 194 ++++++- .../datasource-s3/pom.xml | 21 - .../seatunnel-datasource-plugins/pom.xml | 15 + seatunnel-server/pom.xml | 116 ----- seatunnel-server/seatunnel-app/pom.xml | 43 +- .../app/common/SeaTunnelConnectorI18n.java | 44 ++ .../request/connector/BusinessMode.java | 24 + .../domain/request/job/DataSourceOption.java | 34 ++ .../domain/request/job/SelectTableFields.java | 35 ++ .../app/domain/response/BaseInfo.java | 34 ++ .../response/connector/ConnectorFeature.java | 28 + .../response/connector/ConnectorInfo.java | 30 ++ .../response/connector/DataSourceInfo.java | 30 ++ .../connector/DataSourceInstance.java | 32 ++ .../datasource/DatasourceDetailRes.java | 48 ++ .../response/datasource/DatasourceRes.java | 44 ++ .../datasource/VirtualTableDetailRes.java | 58 +++ .../datasource/VirtualTableFieldRes.java | 54 ++ .../response/datasource/VirtualTableRes.java | 44 ++ .../AbstractDataSourceConfigSwitcher.java | 163 ++++++ .../datasource/DataSourceClientFactory.java | 39 ++ .../datasource/DataSourceConfigSwitcher.java | 57 +++ .../DataSourceConfigSwitcherUtils.java | 135 +++++ .../datasource/SchemaGenerator.java | 90 ++++ .../BaseJdbcDataSourceConfigSwitcher.java | 186 +++++++ .../ClickhouseDataSourceConfigSwitcher.java | 152 ++++++ ...ElasticSearchDataSourceConfigSwitcher.java | 125 +++++ .../impl/KafkaDataSourceConfigSwitcher.java | 129 +++++ .../MysqlCDCDataSourceConfigSwitcher.java | 179 +++++++ .../impl/MysqlDatasourceConfigSwitcher.java | 25 + .../impl/OracleDataSourceConfigSwitcher.java | 67 +++ .../PostgresCDCDataSourceConfigSwitcher.java | 185 +++++++ .../PostgresqlDataSourceConfigSwitcher.java | 60 +++ .../RedshiftDataSourceConfigSwitcher.java | 58 +++ .../impl/S3DataSourceConfigSwitcher.java | 185 +++++++ .../S3RedshiftDataSourceConfigSwitcher.java | 84 +++ .../SqlServerCDCDataSourceConfigSwitcher.java | 185 +++++++ .../SqlServerDataSourceConfigSwitcher.java | 33 ++ .../StarRocksDataSourceConfigSwitcher.java | 109 ++++ .../impl/TidbDataSourceConfigSwitcher.java | 82 +++ .../exceptions/UnSupportWrapperException.java | 30 ++ .../thridparty/framework/FormOptionSort.java | 67 +++ .../framework/PluginDiscoveryUtil.java | 120 +++++ .../framework/SeaTunnelOptionRuleWrapper.java | 484 ++++++++++++++++++ .../framework/UnSupportWrapperException.java | 30 ++ .../apache/seatunnel/app/utils/JdbcUtils.java | 59 +++ seatunnel-web-dist/release-docs/LICENSE | 1 + .../src/main/assembly/seatunnel-web-ci.xml | 13 + .../src/main/assembly/seatunnel-web.xml | 13 + tools/dependencies/known-dependencies.txt | 4 + 51 files changed, 3929 insertions(+), 149 deletions(-) create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/connector/BusinessMode.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/DataSourceOption.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/SelectTableFields.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/BaseInfo.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorFeature.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorInfo.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInfo.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInstance.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceDetailRes.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceRes.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableDetailRes.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableFieldRes.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableRes.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/AbstractDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceClientFactory.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcherUtils.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/SchemaGenerator.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/BaseJdbcDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ClickhouseDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ElasticSearchDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/KafkaDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlCDCDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlDatasourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/OracleDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresCDCDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresqlDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/RedshiftDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3DataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3RedshiftDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerCDCDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/StarRocksDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/TidbDataSourceConfigSwitcher.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/exceptions/UnSupportWrapperException.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/FormOptionSort.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/PluginDiscoveryUtil.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/SeaTunnelOptionRuleWrapper.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/UnSupportWrapperException.java create mode 100644 seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/utils/JdbcUtils.java diff --git a/.licenserc.yaml b/.licenserc.yaml index ec23eba6e..052f0bc1a 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -41,5 +41,6 @@ header: - '**/.gitkeep' - '**/com/typesafe/config/**' - 'seatunnel-main-repository/**' + - 'seatunnel-web-dist/release-docs/**' comment: on-failure diff --git a/pom.xml b/pom.xml index b2a4da102..c015a38de 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,8 @@ 1.2 1.7.25 - 2.12.7.1 + 2.12.7.1 + 2.12.7 8.0.16 2.1.214 5.9.0 @@ -125,6 +126,23 @@ true 3.1.1 1.3.0 + + 2.6.8 + 5.3.20 + 3.5.3.1 + 1.2.9 + 2.6.1 + 1.5.10 + 6.2.2.Final + 1.3.2 + 1.14.3 + 0.10.7 + 9.1.6 + 2.11.0 + + 2.3.1 + 3.1.4 + 1.11.271
    @@ -240,6 +258,28 @@ org.apache.seatunnel seatunnel-plugin-discovery ${seatunnel-framework.version} + + + jcl-over-slf4j + org.slf4j + + + log4j-1.2-api + org.apache.logging.log4j + + + log4j-api + org.apache.logging.log4j + + + log4j-core + org.apache.logging.log4j + + + log4j-slf4j-impl + org.apache.logging.log4j + + @@ -255,6 +295,109 @@ provided + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-jetty + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-aop + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-jdbc + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + test + + + com.google.code.gson + gson + 2.8.6 + + + com.alibaba + druid-spring-boot-starter + ${druid-spring-boot-starter.version} + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus-boot-starter.version} + + + org.springframework.boot + spring-boot-starter-jdbc + + + spring-boot-autoconfigure + org.springframework.boot + + + + + org.hibernate.validator + hibernate-validator + ${hibernate.validator.version} + + + + + io.springfox + springfox-swagger2 + ${springfox-swagger.version} + + + io.springfox + springfox-swagger-ui + ${springfox-swagger.version} + + + io.swagger + swagger-annotations + ${swagger-annotations.version} + + + + + io.jsonwebtoken + jjwt-api + ${jwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jwt.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${jwt.version} + runtime + + + + + org.jsoup + jsoup + ${jsoup.version} + + org.apache.seatunnel @@ -296,6 +439,11 @@ commons-lang3 ${commons-lang3.version} + + commons-io + commons-io + ${commons-io.version} + org.apache.commons commons-collections4 @@ -312,7 +460,7 @@ com.fasterxml.jackson.core jackson-databind - ${jackson.version} + ${jackson-databind.version} @@ -350,6 +498,48 @@ ${awaitility.version} test + + + org.apache.seatunnel + seatunnel-hadoop3-3.1.4-uber + provided + ${hadoop-uber.version} + + + org.apache.avro + avro + + + + + org.apache.hadoop + hadoop-aws + ${hadoop-aws.version} + provided + + + jdk.tools + jdk.tools + + + + + com.amazonaws + aws-java-sdk-bundle + ${aws-java-sdk-bundle.version} + provided + + + com.cronutils + cron-utils + ${cron-utils.version} + + + javassist + org.javassist + + + diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml index be117f58c..59b39e58c 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml @@ -24,12 +24,6 @@ datasource-s3 - - 2.4.4-WS-SNAPSHOT - 3.1.4 - 1.11.271 - - org.apache.seatunnel @@ -39,29 +33,14 @@ org.apache.seatunnel seatunnel-hadoop3-3.1.4-uber - ${hadoop-uber.version} - - - org.apache.avro - avro - - org.apache.hadoop hadoop-aws - ${hadoop-aws.version} - - - jdk.tools - jdk.tools - - com.amazonaws aws-java-sdk-bundle - ${aws-java-sdk-bundle.version} diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml index 039df6b4e..91cdc7409 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml +++ b/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml @@ -1,4 +1,19 @@ + + 4.0.0 diff --git a/seatunnel-server/pom.xml b/seatunnel-server/pom.xml index 7f1e1defa..7bfbf9d67 100644 --- a/seatunnel-server/pom.xml +++ b/seatunnel-server/pom.xml @@ -32,120 +32,4 @@ seatunnel-scheduler seatunnel-server-common - - - 2.6.8 - 5.3.20 - 3.5.3.1 - 1.2.9 - 2.6.1 - 1.5.10 - 6.2.2.Final - 1.3.2 - 1.14.3 - 0.10.7 - 9.1.6 - - - - - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-jetty - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-aop - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-jdbc - ${spring-boot.version} - - - com.google.code.gson - gson - 2.8.6 - - - com.alibaba - druid-spring-boot-starter - ${druid-spring-boot-starter.version} - - - - - com.baomidou - mybatis-plus-boot-starter - ${mybatis-plus-boot-starter.version} - - - org.springframework.boot - spring-boot-starter-jdbc - - - spring-boot-autoconfigure - org.springframework.boot - - - - - org.hibernate.validator - hibernate-validator - ${hibernate.validator.version} - - - - - io.springfox - springfox-swagger2 - ${springfox-swagger.version} - - - io.springfox - springfox-swagger-ui - ${springfox-swagger.version} - - - io.swagger - swagger-annotations - ${swagger-annotations.version} - - - - - io.jsonwebtoken - jjwt-api - ${jwt.version} - - - io.jsonwebtoken - jjwt-impl - ${jwt.version} - runtime - - - io.jsonwebtoken - jjwt-jackson - ${jwt.version} - runtime - - - - - org.jsoup - jsoup - ${jsoup.version} - - - - \ No newline at end of file diff --git a/seatunnel-server/seatunnel-app/pom.xml b/seatunnel-server/seatunnel-app/pom.xml index 7458e5895..ca908b06a 100644 --- a/seatunnel-server/seatunnel-app/pom.xml +++ b/seatunnel-server/seatunnel-app/pom.xml @@ -30,11 +30,19 @@ org.apache.seatunnel seatunnel-common + + org.apache.seatunnel + seatunnel-api + org.apache.seatunnel seatunnel-dynamicform ${project.version} + + org.apache.seatunnel + seatunnel-plugin-discovery + org.apache.seatunnel seatunnel-scheduler-dolphinscheduler @@ -45,6 +53,26 @@ seatunnel-server-common ${project.version} + + + + org.apache.seatunnel + seatunnel-datasource-client + ${project.version} + + + org.apache.seatunnel + datasource-plugins-api + ${project.version} + + + + org.apache.seatunnel + datasource-s3 + ${project.version} + provided + + org.springframework.boot @@ -118,13 +146,11 @@ io.springfox springfox-swagger-ui - ${springfox-swagger.version} io.swagger swagger-annotations - ${swagger-annotations.version} @@ -183,7 +209,6 @@ org.springframework.boot spring-boot-starter-test - ${spring-boot.version} test @@ -204,17 +229,15 @@ spring-boot-starter-aop + + commons-io + commons-io + + com.cronutils cron-utils - ${cron-utils.version} - - - javassist - org.javassist - - \ No newline at end of file diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java new file mode 100644 index 000000000..96e4780b9 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.common; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; + +import org.apache.commons.io.IOUtils; + +import java.nio.charset.StandardCharsets; + +public class SeaTunnelConnectorI18n { + public static Config CONNECTOR_I18N_CONFIG_EN; + public static Config CONNECTOR_I18N_CONFIG_ZH; + + static { + try { + CONNECTOR_I18N_CONFIG_EN = ConfigFactory.parseString(IOUtils.toString(SeaTunnelConnectorI18n.class.getResourceAsStream("/i18n_en.config"), StandardCharsets.UTF_8)); + CONNECTOR_I18N_CONFIG_ZH = + ConfigFactory.parseString( + IOUtils.toString( + SeaTunnelConnectorI18n.class.getResourceAsStream( + "/i18n_zh.config"), + StandardCharsets.UTF_8)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/connector/BusinessMode.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/connector/BusinessMode.java new file mode 100644 index 000000000..73c906c5b --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/connector/BusinessMode.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.request.connector; + +public enum BusinessMode { + DATA_REPLICA, + + DATA_INTEGRATION; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/DataSourceOption.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/DataSourceOption.java new file mode 100644 index 000000000..0cc8ebd9f --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/DataSourceOption.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.request.job; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DataSourceOption { + + private List databases; + + private List tables; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/SelectTableFields.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/SelectTableFields.java new file mode 100644 index 000000000..6c4cd0a0c --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/request/job/SelectTableFields.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.request.job; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SelectTableFields { + + private boolean all; + private List tableFields; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/BaseInfo.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/BaseInfo.java new file mode 100644 index 000000000..3fd2dd600 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/BaseInfo.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response; + +import lombok.Data; + +import java.util.Date; + +@Data +public class BaseInfo { + + private String createUserName; + + private Date createTime; + + private String updateUserName; + + private Date updateTime; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorFeature.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorFeature.java new file mode 100644 index 000000000..a77e95d32 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorFeature.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.connector; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ConnectorFeature { + + private boolean supportColumnProjection; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorInfo.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorInfo.java new file mode 100644 index 000000000..615b2bf0d --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/ConnectorInfo.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.connector; + +import org.apache.seatunnel.plugin.discovery.PluginIdentifier; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ConnectorInfo { + private PluginIdentifier pluginIdentifier; + private String artifactId; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInfo.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInfo.java new file mode 100644 index 000000000..c8cdc9122 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInfo.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.connector; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class DataSourceInfo { + + private ConnectorInfo connectorInfo; + + private String datasourceName; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInstance.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInstance.java new file mode 100644 index 000000000..963056dc2 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/connector/DataSourceInstance.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.connector; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class DataSourceInstance { + + private DataSourceInfo dataSourceInfo; + + private String dataSourceInstanceName; + + private Long dataSourceInstanceId; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceDetailRes.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceDetailRes.java new file mode 100644 index 000000000..4298f4bcf --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceDetailRes.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.datasource; + +import lombok.Data; + +import java.util.Date; +import java.util.Map; + +@Data +public class DatasourceDetailRes { + + private String id; + + private String datasourceName; + + private String pluginName; + + private String pluginVersion; + + // todo check configuration + private Map datasourceConfig; + + private String description; + + private String createUserName; + + private String updateUserName; + + private Date createTime; + + private Date updateTime; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceRes.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceRes.java new file mode 100644 index 000000000..b702a124f --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/DatasourceRes.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.datasource; + +import org.apache.seatunnel.app.domain.response.BaseInfo; + +import lombok.Data; + +import java.util.Map; + +@Data +public class DatasourceRes extends BaseInfo { + + private String id; + + private String datasourceName; + + private String pluginName; + + private String pluginVersion; + + private String description; + + private Map datasourceConfig; + + private int createUserId; + + private int updateUserId; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableDetailRes.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableDetailRes.java new file mode 100644 index 000000000..dba9dc6f1 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableDetailRes.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.datasource; + +import org.apache.seatunnel.app.domain.response.BaseInfo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +@ApiModel(value = "VirtualTable Detail Response", description = "virtual table detail info") +public class VirtualTableDetailRes extends BaseInfo { + + @ApiModelProperty(value = "table id", required = true, dataType = "String") + private String tableId; + + @ApiModelProperty(value = "datasource id", required = true, dataType = "String") + private String datasourceId; + + @ApiModelProperty(value = "datasource name", required = true, dataType = "String") + private String datasourceName; + + @ApiModelProperty(value = "database name", required = true, dataType = "String") + private String databaseName; + + @ApiModelProperty(value = "plugin name", required = true, dataType = "String") + private String pluginName; + + @ApiModelProperty(value = "table name", required = true, dataType = "String") + private String tableName; + + @ApiModelProperty(value = "table description", dataType = "String") + private String description; + + @ApiModelProperty(value = "table properties", dataType = "List") + private List fields; + + private Map datasourceProperties; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableFieldRes.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableFieldRes.java new file mode 100644 index 000000000..09342decd --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableFieldRes.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.datasource; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@ApiModel(value = "VirtualTable Field Response", description = "virtual table field info") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class VirtualTableFieldRes { + + @ApiModelProperty(value = "field name", required = true, dataType = "String") + private String fieldName; + + @ApiModelProperty(value = "field type", required = true, dataType = "String") + private String fieldType; + + @ApiModelProperty(value = "field length", required = true, dataType = "Boolean") + private Boolean nullable; + + @ApiModelProperty(value = "field default value", dataType = "String") + private String defaultValue; + + @ApiModelProperty(value = "primary key", dataType = "Boolean") + private Boolean primaryKey; + + @ApiModelProperty(value = "field comment", dataType = "String") + private String fieldComment; + + @ApiModelProperty(value = "field extra", dataType = "String") + private String fieldExtra; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableRes.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableRes.java new file mode 100644 index 000000000..9d56bfbf9 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/response/datasource/VirtualTableRes.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.domain.response.datasource; + +import org.apache.seatunnel.app.domain.response.BaseInfo; + +import lombok.Data; + +@Data +public class VirtualTableRes extends BaseInfo { + + private String tableId; + + private String datasourceId; + + private String datasourceName; + + private String databaseName; + + private String tableName; + + private String description; + + private String pluginName; + + private int createUserId; + + private int updateUserId; +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/AbstractDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/AbstractDataSourceConfigSwitcher.java new file mode 100644 index 000000000..576b55a8d --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/AbstractDataSourceConfigSwitcher.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.api.configuration.util.RequiredOption; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.exceptions.UnSupportWrapperException; +import org.apache.seatunnel.app.thridparty.framework.SeaTunnelOptionRuleWrapper; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValue; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class AbstractDataSourceConfigSwitcher implements DataSourceConfigSwitcher { + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + + List dataSourceRequiredAllKey = + Stream.concat( + dataSourceOptionRule.getRequiredOptions().stream(), + virtualTableOptionRule.getRequiredOptions().stream()) + .flatMap(ro -> ro.getOptions().stream().map(Option::key)) + .collect(Collectors.toList()); + + dataSourceRequiredAllKey.addAll(excludedKeys); + + List requiredOptions = + connectorOptionRule.getRequiredOptions().stream() + .map( + requiredOption -> { + if (requiredOption instanceof RequiredOption.AbsolutelyRequiredOptions) { + RequiredOption.AbsolutelyRequiredOptions + absolutelyRequiredOptions = + (RequiredOption.AbsolutelyRequiredOptions) + requiredOption; + List> requiredOpList = + absolutelyRequiredOptions.getOptions().stream() + .filter( + op -> { + return !dataSourceRequiredAllKey + .contains(op.key()); + }) + .collect(Collectors.toList()); + return requiredOpList.isEmpty() ? null + : OptionRule.builder() + .required( + requiredOpList.toArray( + new Option[0])) + .build() + .getRequiredOptions() + .get(0); + } + + if (requiredOption instanceof RequiredOption.BundledRequiredOptions) { + List> bundledRequiredOptions = + requiredOption.getOptions(); + return bundledRequiredOptions.stream() + .anyMatch( + op -> + dataSourceRequiredAllKey + .contains(op.key())) ? null + : requiredOption; + } + + if (requiredOption instanceof RequiredOption.ExclusiveRequiredOptions) { + List> exclusiveOptions = + requiredOption.getOptions(); + return exclusiveOptions.stream() + .anyMatch( + op -> + dataSourceRequiredAllKey + .contains(op.key())) ? null + : requiredOption; + } + + if (requiredOption instanceof RequiredOption.ConditionalRequiredOptions) { + List> conditionalRequiredOptions = + requiredOption.getOptions(); + return conditionalRequiredOptions.stream() + .anyMatch( + op -> + dataSourceRequiredAllKey + .contains(op.key())) ? null + : requiredOption; + } + + throw new UnSupportWrapperException( + connectorName, "Unknown", requiredOption.toString()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + List dataSourceOptionAllKey = + Stream.concat( + dataSourceOptionRule.getOptionalOptions().stream(), + virtualTableOptionRule.getOptionalOptions().stream()) + .map(Option::key) + .collect(Collectors.toList()); + + dataSourceOptionAllKey.addAll(excludedKeys); + + List> optionList = + connectorOptionRule.getOptionalOptions().stream() + .filter(option -> !dataSourceOptionAllKey.contains(option.key())) + .collect(Collectors.toList()); + + return SeaTunnelOptionRuleWrapper.wrapper( + optionList, requiredOptions, connectorName, pluginType); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + + Config mergedConfig = connectorConfig; + + for (Map.Entry en : dataSourceInstanceConfig.entrySet()) { + mergedConfig = mergedConfig.withValue(en.getKey(), en.getValue()); + } + + return mergedConfig; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceClientFactory.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceClientFactory.java new file mode 100644 index 000000000..4647e6eb2 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceClientFactory.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource; + +import org.apache.seatunnel.datasource.DataSourceClient; + +public class DataSourceClientFactory { + private static DataSourceClient INSTANCE; + + private static final Object LOCK = new Object(); + + public static DataSourceClient getDataSourceClient() { + if (null != INSTANCE) { + return INSTANCE; + } + synchronized (LOCK) { + if (null != INSTANCE) { + return INSTANCE; + } + INSTANCE = new DataSourceClient(); + return INSTANCE; + } + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcher.java new file mode 100644 index 000000000..d7a4de8ff --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcher.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; + +import java.util.List; + +public interface DataSourceConfigSwitcher { + + /** + * Use the OptionRule of the data source to filter the OptionRule of the connector + */ + FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys); + + /** + * Merge the parameters of the data source instance and connector configuration into the final connector parameters + */ + Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig); +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcherUtils.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcherUtils.java new file mode 100644 index 000000000..2e5b3071d --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/DataSourceConfigSwitcherUtils.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.impl.ClickhouseDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.ElasticSearchDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.KafkaDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.MysqlCDCDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.MysqlDatasourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.OracleDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.PostgresCDCDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.PostgresqlDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.RedshiftDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.S3DataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.S3RedshiftDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.SqlServerCDCDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.SqlServerDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.StarRocksDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.impl.TidbDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; +import org.apache.seatunnel.common.utils.SeaTunnelException; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; + +import java.util.ArrayList; + +public class DataSourceConfigSwitcherUtils { + + public static FormStructure filterOptionRule( + String datasourceName, + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + PluginType pluginType, + BusinessMode businessMode, + OptionRule connectorOptionRule) { + DataSourceConfigSwitcher dataSourceConfigSwitcher = + getDataSourceConfigSwitcher(datasourceName.toUpperCase()); + return dataSourceConfigSwitcher.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + new ArrayList<>()); + } + + public static Config mergeDatasourceConfig( + String datasourceName, + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + DataSourceConfigSwitcher dataSourceConfigSwitcher = + getDataSourceConfigSwitcher(datasourceName.toUpperCase()); + return dataSourceConfigSwitcher.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + private static DataSourceConfigSwitcher getDataSourceConfigSwitcher(String datasourceName) { + checkNotNull(datasourceName, "datasourceName cannot be null"); + // Use SPI + switch (datasourceName.toUpperCase()) { + case "JDBC-MYSQL": + return MysqlDatasourceConfigSwitcher.INSTANCE; + case "ELASTICSEARCH": + return ElasticSearchDataSourceConfigSwitcher.INSTANCE; + case "KAFKA": + return KafkaDataSourceConfigSwitcher.getInstance(); + case "STARROCKS": + return StarRocksDataSourceConfigSwitcher.INSTANCE; + case "MYSQL-CDC": + return MysqlCDCDataSourceConfigSwitcher.INSTANCE; + case "S3-REDSHIFT": + return S3RedshiftDataSourceConfigSwitcher.getInstance(); + case "JDBC-CLICKHOUSE": + return ClickhouseDataSourceConfigSwitcher.getInstance(); + case "JDBC-POSTGRES": + return PostgresqlDataSourceConfigSwitcher.getInstance(); + case "JDBC-REDSHIFT": + return RedshiftDataSourceConfigSwitcher.getInstance(); + case "JDBC-SQLSERVER": + return SqlServerDataSourceConfigSwitcher.getInstance(); + case "JDBC-TIDB": + return TidbDataSourceConfigSwitcher.INSTANCE; + case "JDBC-ORACLE": + return OracleDataSourceConfigSwitcher.INSTANCE; + case "S3": + return S3DataSourceConfigSwitcher.getInstance(); + case "SQLSERVER-CDC": + return SqlServerCDCDataSourceConfigSwitcher.INSTANCE; + case "POSTGRES-CDC": + return PostgresCDCDataSourceConfigSwitcher.INSTANCE; + + default: + throw new SeaTunnelException( + "data source : " + + datasourceName + + " is no implementation class for DataSourceConfigSwitcher"); + } + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/SchemaGenerator.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/SchemaGenerator.java new file mode 100644 index 000000000..730ee658d --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/SchemaGenerator.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableFieldRes; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Slf4j +public class SchemaGenerator { + + private SchemaGenerator() { + } + + /** + * Generate the schema of the table. + * + *
    +     * fields {
    +     *        name = "string"
    +     *        age = "int"
    +     *       }
    +     * 
    + * + * @param virtualTableDetailRes virtual table detail. + * @param selectTableFields select table fields which need to be placed in the schema. + * @return schema. + */ + public static Config generateSchemaBySelectTableFields( + VirtualTableDetailRes virtualTableDetailRes, SelectTableFields selectTableFields) { + checkNotNull(selectTableFields, "selectTableFields cannot be null"); + checkArgument( + CollectionUtils.isNotEmpty(selectTableFields.getTableFields()), + "selectTableFields.tableFields cannot be empty"); + + checkNotNull(virtualTableDetailRes, "virtualTableDetailRes cannot be null"); + checkArgument( + CollectionUtils.isNotEmpty(virtualTableDetailRes.getFields()), + "virtualTableDetailRes.fields cannot be empty"); + + Map fieldTypeMap = + virtualTableDetailRes.getFields().stream() + .collect( + Collectors.toMap( + VirtualTableFieldRes::getFieldName, Function.identity())); + + Config schema = ConfigFactory.empty(); + for (String fieldName : selectTableFields.getTableFields()) { + VirtualTableFieldRes virtualTableFieldRes = + checkNotNull( + fieldTypeMap.get(fieldName), + String.format( + "Cannot find the field: %s from virtual table", fieldName)); + schema = + schema.withValue( + fieldName, + ConfigValueFactory.fromAnyRef(virtualTableFieldRes.getFieldType())); + } + return schema.atKey("fields"); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/BaseJdbcDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/BaseJdbcDataSourceConfigSwitcher.java new file mode 100644 index 000000000..f8bba3dc2 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/BaseJdbcDataSourceConfigSwitcher.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class BaseJdbcDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + private static final String TABLE_KEY = "table"; + private static final String DATABASE_KEY = "database"; + + private static final String QUERY_KEY = "query"; + + private static final String GENERATE_SINK_SQL = "generate_sink_sql"; + + private static final String URL_KEY = "url"; + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + Map> filterFieldMap = new HashMap<>(); + + filterFieldMap.put( + PluginType.SINK, + Arrays.asList(QUERY_KEY, TABLE_KEY, DATABASE_KEY, GENERATE_SINK_SQL)); + filterFieldMap.put(PluginType.SOURCE, Collections.singletonList(QUERY_KEY)); + + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + filterFieldMap.get(pluginType)); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + + // replace database in url + if (dataSourceOption.getDatabases().size() == 1) { + String databaseName = dataSourceOption.getDatabases().get(0); + String url = dataSourceInstanceConfig.getString(URL_KEY); + String newUrl = replaceDatabaseNameInUrl(url, databaseName); + dataSourceInstanceConfig = + dataSourceInstanceConfig.withValue( + URL_KEY, ConfigValueFactory.fromAnyRef(newUrl)); + } + if (pluginType.equals(PluginType.SINK)) { + connectorConfig = + connectorConfig.withValue( + GENERATE_SINK_SQL, ConfigValueFactory.fromAnyRef(false)); + } + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + + String databaseName = dataSourceOption.getDatabases().get(0); + + String tableName = dataSourceOption.getTables().get(0); + + // create sql from schema + if (pluginType.equals(PluginType.SOURCE)) { + + List tableFields = selectTableFields.getTableFields(); + + String sql = tableFieldsToSql(tableFields, databaseName, tableName); + + connectorConfig = + connectorConfig.withValue(QUERY_KEY, ConfigValueFactory.fromAnyRef(sql)); + } else if (pluginType.equals(PluginType.SINK)) { + connectorConfig = + connectorConfig.withValue( + DATABASE_KEY, ConfigValueFactory.fromAnyRef(databaseName)); + connectorConfig = + connectorConfig.withValue( + TABLE_KEY, ConfigValueFactory.fromAnyRef(tableName)); + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } else if (businessMode.equals(BusinessMode.DATA_REPLICA)) { + String databaseName = dataSourceOption.getDatabases().get(0); + if (pluginType.equals(PluginType.SINK)) { + connectorConfig = + connectorConfig.withValue( + DATABASE_KEY, ConfigValueFactory.fromAnyRef(databaseName)); + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } else { + throw new UnsupportedOperationException( + "JDBC DATA_REPLICA Unsupported plugin type: " + pluginType); + } + + } else { + throw new UnsupportedOperationException("Unsupported businessMode : " + businessMode); + } + } + + protected String generateSql( + List tableFields, String database, String schema, String table) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT "); + for (int i = 0; i < tableFields.size(); i++) { + sb.append(quoteIdentifier(tableFields.get(i))); + if (i < tableFields.size() - 1) { + sb.append(", "); + } + } + sb.append(" FROM ").append(quoteIdentifier(database)); + if (schema != null && !schema.isEmpty()) { + sb.append(".").append(quoteIdentifier(schema)); + } + sb.append(".").append(quoteIdentifier(table)); + + return sb.toString(); + } + + protected String tableFieldsToSql(List tableFields, String database, String table) { + return generateSql(tableFields, database, null, table); + } + + protected String quoteIdentifier(String identifier) { + return "`" + identifier + "`"; + } + + protected String replaceDatabaseNameInUrl(String url, String databaseName) { + return url; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ClickhouseDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ClickhouseDataSourceConfigSwitcher.java new file mode 100644 index 000000000..72fb36b76 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ClickhouseDataSourceConfigSwitcher.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.app.utils.JdbcUtils; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +import java.util.List; +import java.util.Map; + +public class ClickhouseDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + private static final ClickhouseDataSourceConfigSwitcher INSTANCE = + new ClickhouseDataSourceConfigSwitcher(); + + private static final String HOST = "host"; + private static final String URL = "url"; + private static final String SQL = "sql"; + private static final String DATABASE = "database"; + private static final String TABLE = "table"; + + private static final Map> FILTER_FIELD_MAP = + new ImmutableMap.Builder>() + .put(PluginType.SOURCE, Lists.newArrayList(SQL, HOST)) + .put(PluginType.SINK, Lists.newArrayList(HOST, DATABASE, TABLE)) + .build(); + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + FILTER_FIELD_MAP.get(pluginType)); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + switch (businessMode) { + case DATA_REPLICA: + // We only support sink in data replica mode + if (pluginType.equals(PluginType.SINK)) { + connectorConfig = + connectorConfig.withValue( + DATABASE, + ConfigValueFactory.fromAnyRef( + dataSourceOption.getDatabases().get(0))); + } else { + throw new UnsupportedOperationException( + "Clickhouse DATA_REPLICA Unsupported plugin type: " + pluginType); + } + break; + case DATA_INTEGRATION: + // generate the sql by the schema + if (pluginType.equals(PluginType.SOURCE)) { + List tableFields = selectTableFields.getTableFields(); + String sql = + String.format( + "SELECT %s FROM %s", + String.join(",", tableFields), + String.format( + "`%s`.`%s`", + dataSourceOption.getDatabases().get(0), + dataSourceOption.getTables().get(0))); + connectorConfig = + connectorConfig.withValue(SQL, ConfigValueFactory.fromAnyRef(sql)); + } else if (pluginType.equals(PluginType.SINK)) { + connectorConfig = + connectorConfig.withValue( + DATABASE, + ConfigValueFactory.fromAnyRef( + dataSourceOption.getDatabases().get(0))); + connectorConfig = + connectorConfig.withValue( + TABLE, + ConfigValueFactory.fromAnyRef( + dataSourceOption.getTables().get(0))); + } else { + throw new UnsupportedOperationException( + "Unsupported plugin type: " + pluginType); + } + break; + default: + break; + } + connectorConfig = + connectorConfig.withValue( + HOST, + ConfigValueFactory.fromAnyRef( + JdbcUtils.getAddressFromUrl( + dataSourceInstanceConfig.getString(URL)))); + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + public static ClickhouseDataSourceConfigSwitcher getInstance() { + return INSTANCE; + } + + private ClickhouseDataSourceConfigSwitcher() { + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ElasticSearchDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ElasticSearchDataSourceConfigSwitcher.java new file mode 100644 index 000000000..a378fae71 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/ElasticSearchDataSourceConfigSwitcher.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import java.util.Arrays; +import java.util.List; + +public class ElasticSearchDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + public static final ElasticSearchDataSourceConfigSwitcher INSTANCE = + new ElasticSearchDataSourceConfigSwitcher(); + + private static final String SOURCE = "source"; + private static final String SCHEMA = "schema"; + private static final String PRIMARY_KEYS = "primary_keys"; + private static final String INDEX = "index"; + + private ElasticSearchDataSourceConfigSwitcher() { + } + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + if (PluginType.SOURCE.equals(pluginType)) { + // DELETE source/schema + excludedKeys.addAll(Arrays.asList(SOURCE, SCHEMA, INDEX)); + } else if (PluginType.SINK.equals(pluginType)) { + excludedKeys.add(INDEX); + // DELETE primary_keys + if (businessMode.equals(BusinessMode.DATA_REPLICA)) { + excludedKeys.add(PRIMARY_KEYS); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + if (PluginType.SOURCE.equals(pluginType)) { + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + // Add source + connectorConfig = + connectorConfig.withValue( + INDEX, + ConfigValueFactory.fromAnyRef(dataSourceOption.getTables().get(0))); + connectorConfig = + connectorConfig.withValue( + SOURCE, + ConfigValueFactory.fromIterable( + selectTableFields.getTableFields())); + } else { + throw new UnsupportedOperationException( + "Unsupported business mode: " + businessMode); + } + } else if (PluginType.SINK.equals(pluginType)) { + // TODO Add primary_keys + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + // Add Index + connectorConfig = + connectorConfig.withValue( + INDEX, + ConfigValueFactory.fromAnyRef(dataSourceOption.getTables().get(0))); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/KafkaDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/KafkaDataSourceConfigSwitcher.java new file mode 100644 index 000000000..3e7b5fddd --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/KafkaDataSourceConfigSwitcher.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.SchemaGenerator; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import java.util.List; + +public class KafkaDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private static final KafkaDataSourceConfigSwitcher INSTANCE = + new KafkaDataSourceConfigSwitcher(); + + private static final String SCHEMA = "schema"; + private static final String TOPIC = "topic"; + private static final String TABLE = "table"; + private static final String FORMAT = "format"; + + private static final String DEBEZIUM_FORMAT = "COMPATIBLE_DEBEZIUM_JSON"; + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + if (pluginType == PluginType.SOURCE) { + excludedKeys.add(SCHEMA); + excludedKeys.add(TOPIC); + } + if (pluginType == PluginType.SINK && businessMode.equals(BusinessMode.DATA_REPLICA)) { + excludedKeys.add(FORMAT); + } + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + if (pluginType == PluginType.SOURCE) { + // Use field to generate the schema + connectorConfig = + connectorConfig.withValue( + TOPIC, + ConfigValueFactory.fromAnyRef( + virtualTableDetail.getDatasourceProperties().get(TOPIC))); + connectorConfig = + connectorConfig.withValue( + SCHEMA, + SchemaGenerator.generateSchemaBySelectTableFields( + virtualTableDetail, selectTableFields) + .root()); + } else if (pluginType == PluginType.SINK) { + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + // Set the table name to topic + connectorConfig = + connectorConfig.withValue( + TOPIC, + ConfigValueFactory.fromAnyRef( + virtualTableDetail.getDatasourceProperties().get(TOPIC))); + } + if (businessMode.equals(BusinessMode.DATA_REPLICA)) { + connectorConfig = + connectorConfig.withValue( + FORMAT, ConfigValueFactory.fromAnyRef(DEBEZIUM_FORMAT)); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + private KafkaDataSourceConfigSwitcher() { + } + + public static KafkaDataSourceConfigSwitcher getInstance() { + return INSTANCE; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlCDCDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlCDCDataSourceConfigSwitcher.java new file mode 100644 index 000000000..2d99e9bdf --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlCDCDataSourceConfigSwitcher.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableFieldRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class MysqlCDCDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private MysqlCDCDataSourceConfigSwitcher() { + } + + public static final MysqlCDCDataSourceConfigSwitcher INSTANCE = + new MysqlCDCDataSourceConfigSwitcher(); + + private static final String FACTORY = "factory"; + + private static final String CATALOG = "catalog"; + + private static final String TABLE_NAMES = "table-names"; + + private static final String DATABASE_NAMES = "database-names"; + + private static final String FORMAT_KEY = "format"; + + private static final String DEBEZIUM_FORMAT = "COMPATIBLE_DEBEZIUM_JSON"; + + private static final String DEFAULT_FORMAT = "DEFAULT"; + + private static final String SCHEMA = "schema"; + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + if (PluginType.SOURCE.equals(pluginType)) { + excludedKeys.add(DATABASE_NAMES); + excludedKeys.add(TABLE_NAMES); + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + excludedKeys.add(FORMAT_KEY); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + if (PluginType.SOURCE.equals(pluginType)) { + // Add table-names + Config config = ConfigFactory.empty(); + config = config.withValue(FACTORY, ConfigValueFactory.fromAnyRef("Mysql")); + connectorConfig = connectorConfig.withValue(CATALOG, config.root()); + connectorConfig = + connectorConfig.withValue( + DATABASE_NAMES, + ConfigValueFactory.fromIterable(dataSourceOption.getDatabases())); + connectorConfig = + connectorConfig.withValue( + TABLE_NAMES, + ConfigValueFactory.fromIterable( + mergeDatabaseAndTables(dataSourceOption))); + + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + connectorConfig = + connectorConfig.withValue( + FORMAT_KEY, ConfigValueFactory.fromAnyRef(DEFAULT_FORMAT)); + } else if (businessMode.equals(BusinessMode.DATA_REPLICA) + && connectorConfig + .getString(FORMAT_KEY) + .toUpperCase(Locale.ROOT) + .equals(DEBEZIUM_FORMAT)) { + connectorConfig = + connectorConfig.withValue(SCHEMA, generateDebeziumFormatSchema().root()); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + private Config generateDebeziumFormatSchema() { + List fieldResList = new ArrayList<>(); + fieldResList.add(new VirtualTableFieldRes("topic", "string", false, null, false, "", "")); + fieldResList.add(new VirtualTableFieldRes("key", "string", false, null, false, "", "")); + fieldResList.add(new VirtualTableFieldRes("value", "string", false, null, false, "", "")); + + Config schema = ConfigFactory.empty(); + for (VirtualTableFieldRes virtualTableFieldRes : fieldResList) { + schema = + schema.withValue( + virtualTableFieldRes.getFieldName(), + ConfigValueFactory.fromAnyRef(virtualTableFieldRes.getFieldType())); + } + return schema.atKey("fields"); + } + + private List mergeDatabaseAndTables(DataSourceOption dataSourceOption) { + List tables = new ArrayList<>(); + dataSourceOption + .getDatabases() + .forEach( + database -> { + dataSourceOption + .getTables() + .forEach( + table -> { + if (table.contains(".")) { + tables.add(table); + } else { + tables.add( + getDatabaseAndTable(database, table)); + } + }); + }); + return tables; + } + + private String getDatabaseAndTable(String database, String table) { + return String.format("%s.%s", database, table); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlDatasourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlDatasourceConfigSwitcher.java new file mode 100644 index 000000000..c27d323d0 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/MysqlDatasourceConfigSwitcher.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +public class MysqlDatasourceConfigSwitcher extends BaseJdbcDataSourceConfigSwitcher { + public static MysqlDatasourceConfigSwitcher INSTANCE = new MysqlDatasourceConfigSwitcher(); + + private MysqlDatasourceConfigSwitcher() { + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/OracleDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/OracleDataSourceConfigSwitcher.java new file mode 100644 index 000000000..43d85c84b --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/OracleDataSourceConfigSwitcher.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.common.utils.SeaTunnelException; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class OracleDataSourceConfigSwitcher extends BaseJdbcDataSourceConfigSwitcher { + public static final OracleDataSourceConfigSwitcher INSTANCE = + new OracleDataSourceConfigSwitcher(); + + protected String tableFieldsToSql(List tableFields, String database, String fullTable) { + String[] split = fullTable.split("\\."); + if (split.length != 2) { + throw new SeaTunnelException( + "The tableName for oracle must be schemaName.tableName, but tableName is " + + fullTable); + } + + String schemaName = split[0]; + String tableName = split[1]; + + return generateSql(tableFields, database, schemaName, tableName); + } + + protected String quoteIdentifier(String identifier) { + return "\"" + identifier + "\""; + } + + @Override + protected String generateSql( + List tableFields, String database, String schema, String table) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT "); + for (int i = 0; i < tableFields.size(); i++) { + sb.append(quoteIdentifier(tableFields.get(i))); + if (i < tableFields.size() - 1) { + sb.append(", "); + } + } + sb.append(" FROM ") + .append(quoteIdentifier(schema)) + .append(".") + .append(quoteIdentifier(table)); + return sb.toString(); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresCDCDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresCDCDataSourceConfigSwitcher.java new file mode 100644 index 000000000..1b282dc09 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresCDCDataSourceConfigSwitcher.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableFieldRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class PostgresCDCDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private PostgresCDCDataSourceConfigSwitcher() { + } + + public static final PostgresCDCDataSourceConfigSwitcher INSTANCE = + new PostgresCDCDataSourceConfigSwitcher(); + + private static final String FACTORY = "factory"; + + private static final String CATALOG = "catalog"; + + private static final String TABLE_NAMES = "table-names"; + + private static final String DATABASE_NAMES = "database-names"; + + private static final String FORMAT_KEY = "format"; + + private static final String DEBEZIUM_FORMAT = "COMPATIBLE_DEBEZIUM_JSON"; + + private static final String DEFAULT_FORMAT = "DEFAULT"; + + private static final String SCHEMA = "schema"; + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + if (PluginType.SOURCE.equals(pluginType)) { + excludedKeys.add(DATABASE_NAMES); + excludedKeys.add(TABLE_NAMES); + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + excludedKeys.add(FORMAT_KEY); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + if (PluginType.SOURCE.equals(pluginType)) { + // Add table-names + Config config = ConfigFactory.empty(); + config = config.withValue(FACTORY, ConfigValueFactory.fromAnyRef("Postgres")); + connectorConfig = connectorConfig.withValue(CATALOG, config.root()); + connectorConfig = + connectorConfig.withValue( + DATABASE_NAMES, + ConfigValueFactory.fromIterable(dataSourceOption.getDatabases())); + connectorConfig = + connectorConfig.withValue( + TABLE_NAMES, + ConfigValueFactory.fromIterable( + mergeDatabaseAndTables(dataSourceOption))); + + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + connectorConfig = + connectorConfig.withValue( + FORMAT_KEY, ConfigValueFactory.fromAnyRef(DEFAULT_FORMAT)); + } else if (businessMode.equals(BusinessMode.DATA_REPLICA) + && connectorConfig + .getString(FORMAT_KEY) + .toUpperCase(Locale.ROOT) + .equals(DEBEZIUM_FORMAT)) { + connectorConfig = + connectorConfig.withValue(SCHEMA, generateDebeziumFormatSchema().root()); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + private Config generateDebeziumFormatSchema() { + List fieldResList = new ArrayList<>(); + fieldResList.add(new VirtualTableFieldRes("topic", "string", false, null, false, "", "")); + fieldResList.add(new VirtualTableFieldRes("key", "string", false, null, false, "", "")); + fieldResList.add(new VirtualTableFieldRes("value", "string", false, null, false, "", "")); + + Config schema = ConfigFactory.empty(); + for (VirtualTableFieldRes virtualTableFieldRes : fieldResList) { + schema = + schema.withValue( + virtualTableFieldRes.getFieldName(), + ConfigValueFactory.fromAnyRef(virtualTableFieldRes.getFieldType())); + } + return schema.atKey("fields"); + } + + @SuppressWarnings("checkstyle:MagicNumber") + private List mergeDatabaseAndTables(DataSourceOption dataSourceOption) { + List tables = new ArrayList<>(); + dataSourceOption + .getDatabases() + .forEach( + database -> { + dataSourceOption + .getTables() + .forEach( + table -> { + final String[] tableFragments = table.split("\\."); + if (tableFragments.length == 3) { + tables.add(table); + } else if (tableFragments.length == 2) { + tables.add( + getDatabaseAndTable(database, table)); + } else { + throw new IllegalArgumentException( + "Illegal postgres table-name: " + + table); + } + }); + }); + return tables; + } + + private String getDatabaseAndTable(String database, String table) { + return String.format("%s.%s", database, table); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresqlDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresqlDataSourceConfigSwitcher.java new file mode 100644 index 000000000..90a983d9d --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/PostgresqlDataSourceConfigSwitcher.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.app.thridparty.datasource.DataSourceConfigSwitcher; +import org.apache.seatunnel.app.utils.JdbcUtils; +import org.apache.seatunnel.common.utils.SeaTunnelException; + +import java.util.List; + +public class PostgresqlDataSourceConfigSwitcher extends BaseJdbcDataSourceConfigSwitcher { + + private static final PostgresqlDataSourceConfigSwitcher INSTANCE = + new PostgresqlDataSourceConfigSwitcher(); + + private PostgresqlDataSourceConfigSwitcher() { + } + + protected String tableFieldsToSql(List tableFields, String database, String fullTable) { + + String[] split = fullTable.split("\\."); + if (split.length != 2) { + throw new SeaTunnelException( + "The tableName for postgres must be schemaName.tableName, but tableName is " + + fullTable); + } + + String schemaName = split[0]; + String tableName = split[1]; + + return generateSql(tableFields, database, schemaName, tableName); + } + + protected String quoteIdentifier(String identifier) { + return "\"" + identifier + "\""; + } + + protected String replaceDatabaseNameInUrl(String url, String databaseName) { + return JdbcUtils.replaceDatabase(url, databaseName); + } + + public static DataSourceConfigSwitcher getInstance() { + return INSTANCE; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/RedshiftDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/RedshiftDataSourceConfigSwitcher.java new file mode 100644 index 000000000..98da884c3 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/RedshiftDataSourceConfigSwitcher.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.app.utils.JdbcUtils; +import org.apache.seatunnel.common.utils.SeaTunnelException; + +import java.util.List; + +public class RedshiftDataSourceConfigSwitcher extends BaseJdbcDataSourceConfigSwitcher { + private static final RedshiftDataSourceConfigSwitcher INSTANCE = + new RedshiftDataSourceConfigSwitcher(); + + public static final RedshiftDataSourceConfigSwitcher getInstance() { + return INSTANCE; + } + + protected String tableFieldsToSql(List tableFields, String database, String fullTable) { + + String[] split = fullTable.split("\\."); + if (split.length != 2) { + throw new SeaTunnelException( + "The tableName for postgres must be schemaName.tableName, but tableName is " + + fullTable); + } + + String schemaName = split[0]; + String tableName = split[1]; + + return generateSql(tableFields, database, schemaName, tableName); + } + + protected String quoteIdentifier(String identifier) { + return "\"" + identifier + "\""; + } + + protected String replaceDatabaseNameInUrl(String url, String databaseName) { + return JdbcUtils.replaceDatabase(url, databaseName); + } + + private RedshiftDataSourceConfigSwitcher() { + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3DataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3DataSourceConfigSwitcher.java new file mode 100644 index 000000000..d0c30cf07 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3DataSourceConfigSwitcher.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.app.thridparty.datasource.SchemaGenerator; +import org.apache.seatunnel.common.constants.PluginType; +import org.apache.seatunnel.datasource.plugin.s3.S3OptionRule; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +public class S3DataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private S3DataSourceConfigSwitcher() { + } + + private static final S3DataSourceConfigSwitcher INSTANCE = new S3DataSourceConfigSwitcher(); + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + excludedKeys.add(S3OptionRule.PATH.key()); + if (PluginType.SOURCE.equals(pluginType)) { + excludedKeys.add(S3OptionRule.SCHEMA.key()); + } + + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + if (PluginType.SOURCE.equals(pluginType)) { + connectorConfig = + connectorConfig + .withValue( + S3OptionRule.SCHEMA.key(), + SchemaGenerator.generateSchemaBySelectTableFields( + virtualTableDetail, selectTableFields) + .root()) + .withValue( + S3OptionRule.PATH.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.PATH.key()))) + .withValue( + S3OptionRule.TYPE.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.TYPE.key()))) + .withValue( + S3OptionRule.PARSE_PARSE_PARTITION_FROM_PATH.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get( + S3OptionRule + .PARSE_PARSE_PARTITION_FROM_PATH + .key()))) + .withValue( + S3OptionRule.DATE_FORMAT.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.DATE_FORMAT.key()))) + .withValue( + S3OptionRule.DATETIME_FORMAT.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.DATETIME_FORMAT.key()))) + .withValue( + S3OptionRule.TIME_FORMAT.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.TIME_FORMAT.key()))); + } else if (PluginType.SINK.equals(pluginType)) { + if (virtualTableDetail.getDatasourceProperties().get(S3OptionRule.TIME_FORMAT.key()) == null) { + throw new IllegalArgumentException("S3 virtual table path is null"); + } + connectorConfig = + connectorConfig + .withValue( + S3OptionRule.PATH.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.PATH.key()))) + .withValue( + S3OptionRule.TYPE.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.TYPE.key()))) + .withValue( + S3OptionRule.PARSE_PARSE_PARTITION_FROM_PATH.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get( + S3OptionRule + .PARSE_PARSE_PARTITION_FROM_PATH + .key()))) + .withValue( + S3OptionRule.DATE_FORMAT.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.DATE_FORMAT.key()))) + .withValue( + S3OptionRule.DATETIME_FORMAT.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.DATETIME_FORMAT.key()))) + .withValue( + S3OptionRule.TIME_FORMAT.key(), + ConfigValueFactory.fromAnyRef( + virtualTableDetail + .getDatasourceProperties() + .get(S3OptionRule.TIME_FORMAT.key()))); + } + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + public static S3DataSourceConfigSwitcher getInstance() { + return INSTANCE; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3RedshiftDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3RedshiftDataSourceConfigSwitcher.java new file mode 100644 index 000000000..dd7b208a4 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/S3RedshiftDataSourceConfigSwitcher.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; + +import java.util.List; + +public class S3RedshiftDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private S3RedshiftDataSourceConfigSwitcher() { + } + + private static final S3RedshiftDataSourceConfigSwitcher INSTANCE = + new S3RedshiftDataSourceConfigSwitcher(); + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + excludedKeys.add("access_key"); + excludedKeys.add("secret_key"); + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + public static S3RedshiftDataSourceConfigSwitcher getInstance() { + return INSTANCE; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerCDCDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerCDCDataSourceConfigSwitcher.java new file mode 100644 index 000000000..807d6e4c8 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerCDCDataSourceConfigSwitcher.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableFieldRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class SqlServerCDCDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private SqlServerCDCDataSourceConfigSwitcher() { + } + + public static final SqlServerCDCDataSourceConfigSwitcher INSTANCE = + new SqlServerCDCDataSourceConfigSwitcher(); + + private static final String FACTORY = "factory"; + + private static final String CATALOG = "catalog"; + + private static final String TABLE_NAMES = "table-names"; + + private static final String DATABASE_NAMES = "database-names"; + + private static final String FORMAT_KEY = "format"; + + private static final String DEBEZIUM_FORMAT = "COMPATIBLE_DEBEZIUM_JSON"; + + private static final String DEFAULT_FORMAT = "DEFAULT"; + + private static final String SCHEMA = "schema"; + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + if (PluginType.SOURCE.equals(pluginType)) { + excludedKeys.add(DATABASE_NAMES); + excludedKeys.add(TABLE_NAMES); + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + excludedKeys.add(FORMAT_KEY); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + if (PluginType.SOURCE.equals(pluginType)) { + // Add table-names + Config config = ConfigFactory.empty(); + config = config.withValue(FACTORY, ConfigValueFactory.fromAnyRef("SqlServer")); + connectorConfig = connectorConfig.withValue(CATALOG, config.root()); + connectorConfig = + connectorConfig.withValue( + DATABASE_NAMES, + ConfigValueFactory.fromIterable(dataSourceOption.getDatabases())); + connectorConfig = + connectorConfig.withValue( + TABLE_NAMES, + ConfigValueFactory.fromIterable( + mergeDatabaseAndTables(dataSourceOption))); + + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + connectorConfig = + connectorConfig.withValue( + FORMAT_KEY, ConfigValueFactory.fromAnyRef(DEFAULT_FORMAT)); + } else if (businessMode.equals(BusinessMode.DATA_REPLICA) + && connectorConfig + .getString(FORMAT_KEY) + .toUpperCase(Locale.ROOT) + .equals(DEBEZIUM_FORMAT)) { + connectorConfig = + connectorConfig.withValue(SCHEMA, generateDebeziumFormatSchema().root()); + } + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } + + private Config generateDebeziumFormatSchema() { + List fieldResList = new ArrayList<>(); + fieldResList.add(new VirtualTableFieldRes("topic", "string", false, null, false, "", "")); + fieldResList.add(new VirtualTableFieldRes("key", "string", false, null, false, "", "")); + fieldResList.add(new VirtualTableFieldRes("value", "string", false, null, false, "", "")); + + Config schema = ConfigFactory.empty(); + for (VirtualTableFieldRes virtualTableFieldRes : fieldResList) { + schema = + schema.withValue( + virtualTableFieldRes.getFieldName(), + ConfigValueFactory.fromAnyRef(virtualTableFieldRes.getFieldType())); + } + return schema.atKey("fields"); + } + + @SuppressWarnings("checkstyle:MagicNumber") + private List mergeDatabaseAndTables(DataSourceOption dataSourceOption) { + List tables = new ArrayList<>(); + dataSourceOption + .getDatabases() + .forEach( + database -> { + dataSourceOption + .getTables() + .forEach( + table -> { + final String[] tableFragments = table.split("\\."); + if (tableFragments.length == 3) { + tables.add(table); + } else if (tableFragments.length == 2) { + tables.add( + getDatabaseAndTable(database, table)); + } else { + throw new IllegalArgumentException( + "Illegal sqlserver table-name: " + + table); + } + }); + }); + return tables; + } + + private String getDatabaseAndTable(String database, String table) { + return String.format("%s.%s", database, table); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerDataSourceConfigSwitcher.java new file mode 100644 index 000000000..d5d492f7d --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/SqlServerDataSourceConfigSwitcher.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; + +public class SqlServerDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private static final SqlServerDataSourceConfigSwitcher INSTANCE = + new SqlServerDataSourceConfigSwitcher(); + + public static final SqlServerDataSourceConfigSwitcher getInstance() { + return INSTANCE; + } + + private SqlServerDataSourceConfigSwitcher() { + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/StarRocksDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/StarRocksDataSourceConfigSwitcher.java new file mode 100644 index 000000000..9cd112657 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/StarRocksDataSourceConfigSwitcher.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.thridparty.datasource.AbstractDataSourceConfigSwitcher; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +import java.util.Arrays; +import java.util.List; + +public class StarRocksDataSourceConfigSwitcher extends AbstractDataSourceConfigSwitcher { + + private static final String TABLE = "table"; + + private static final String DATABASE = "database"; + + private StarRocksDataSourceConfigSwitcher() { + } + + public static final StarRocksDataSourceConfigSwitcher INSTANCE = + new StarRocksDataSourceConfigSwitcher(); + + @Override + public FormStructure filterOptionRule( + String connectorName, + OptionRule dataSourceOptionRule, + OptionRule virtualTableOptionRule, + BusinessMode businessMode, + PluginType pluginType, + OptionRule connectorOptionRule, + List excludedKeys) { + if (PluginType.SOURCE.equals(pluginType)) { + throw new UnsupportedOperationException("Unsupported PluginType: " + pluginType); + } else if (PluginType.SINK.equals(pluginType)) { + excludedKeys.addAll(Arrays.asList(TABLE, DATABASE)); + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + return super.filterOptionRule( + connectorName, + dataSourceOptionRule, + virtualTableOptionRule, + businessMode, + pluginType, + connectorOptionRule, + excludedKeys); + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + if (PluginType.SOURCE.equals(pluginType)) { + throw new UnsupportedOperationException("Unsupported PluginType: " + pluginType); + } else if (PluginType.SINK.equals(pluginType)) { + if (businessMode.equals(BusinessMode.DATA_INTEGRATION)) { + // Add Table + connectorConfig = + connectorConfig.withValue( + TABLE, + ConfigValueFactory.fromAnyRef(dataSourceOption.getTables().get(0))); + } + connectorConfig = + connectorConfig.withValue( + DATABASE, + ConfigValueFactory.fromAnyRef(dataSourceOption.getDatabases().get(0))); + } else { + throw new UnsupportedOperationException("Unsupported plugin type: " + pluginType); + } + + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/TidbDataSourceConfigSwitcher.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/TidbDataSourceConfigSwitcher.java new file mode 100644 index 000000000..17dfcef61 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/datasource/impl/TidbDataSourceConfigSwitcher.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.datasource.impl; + +import org.apache.seatunnel.app.domain.request.connector.BusinessMode; +import org.apache.seatunnel.app.domain.request.job.DataSourceOption; +import org.apache.seatunnel.app.domain.request.job.SelectTableFields; +import org.apache.seatunnel.app.domain.response.datasource.VirtualTableDetailRes; +import org.apache.seatunnel.common.constants.PluginType; + +import org.apache.seatunnel.shade.com.typesafe.config.Config; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigValueFactory; + +public class TidbDataSourceConfigSwitcher extends BaseJdbcDataSourceConfigSwitcher { + public static final TidbDataSourceConfigSwitcher INSTANCE = new TidbDataSourceConfigSwitcher(); + + private static final String FACTORY = "factory"; + + private static final String CATALOG = "catalog"; + private static final String USERNAME = "username"; + private static final String PASSWORD = "password"; + private static final String BASE_URL = "base-url"; + + private static final String TABLE_NAMES = "table-names"; + + private static final String DATABASE_NAMES = "database-names"; + + private TidbDataSourceConfigSwitcher() { + } + + @Override + public Config mergeDatasourceConfig( + Config dataSourceInstanceConfig, + VirtualTableDetailRes virtualTableDetail, + DataSourceOption dataSourceOption, + SelectTableFields selectTableFields, + BusinessMode businessMode, + PluginType pluginType, + Config connectorConfig) { + // Add TiDB catalog options + Config config = ConfigFactory.empty(); + config = config.withValue(FACTORY, ConfigValueFactory.fromAnyRef("TiDB")); + config = + config.withValue( + USERNAME, + ConfigValueFactory.fromAnyRef(dataSourceInstanceConfig.getString("user"))); + config = + config.withValue( + PASSWORD, + ConfigValueFactory.fromAnyRef( + dataSourceInstanceConfig.getString(PASSWORD))); + config = + config.withValue( + BASE_URL, + ConfigValueFactory.fromAnyRef(dataSourceInstanceConfig.getString("url"))); + connectorConfig = connectorConfig.withValue(CATALOG, config.root()); + return super.mergeDatasourceConfig( + dataSourceInstanceConfig, + virtualTableDetail, + dataSourceOption, + selectTableFields, + businessMode, + pluginType, + connectorConfig); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/exceptions/UnSupportWrapperException.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/exceptions/UnSupportWrapperException.java new file mode 100644 index 000000000..c761c9db1 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/exceptions/UnSupportWrapperException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.exceptions; + +import lombok.NonNull; + +public class UnSupportWrapperException extends RuntimeException { + public UnSupportWrapperException( + @NonNull String formName, @NonNull String label, @NonNull String typeName) { + super( + String.format( + "Form: %s, label: %s, typeName: %s not yet supported", + formName, label, typeName)); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/FormOptionSort.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/FormOptionSort.java new file mode 100644 index 000000000..63d69ddb5 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/FormOptionSort.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.framework; + +import org.apache.seatunnel.app.dynamicforms.AbstractFormOption; +import org.apache.seatunnel.app.dynamicforms.Constants; +import org.apache.seatunnel.app.dynamicforms.FormStructure; + +import lombok.NonNull; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class FormOptionSort { + /** + * Sort form structure. + */ + public static FormStructure sortFormStructure(@NonNull FormStructure formStructure) { + List newFormOptions = new ArrayList<>(); + List formOptions = formStructure.getForms(); + formOptions.forEach(currFormOption -> { + if (currFormOption.getShow() != null && currFormOption.getShow().size() > 0) { + return; + } + addShowOptionAfter(currFormOption, formOptions, newFormOptions); + }); + + return FormStructure.builder().name(formStructure.getName()).withLocale(formStructure.getLocales()).addFormOption(newFormOptions.toArray(new AbstractFormOption[0])).build(); + } + + public static void addShowOptionAfter(@NonNull AbstractFormOption currFormOption, @NonNull List allFormOptions, @NonNull List newFormOptions) { + if (newFormOptions.contains(currFormOption)) { + return; + } + + newFormOptions.add(currFormOption); + + List showOptions = allFormOptions.stream().filter(nextOption -> { + return nextOption.getShow() != null && nextOption.getShow().size() > 0 && nextOption.getShow().get(Constants.SHOW_FIELD).toString().equals(currFormOption.getField()); + }).collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(showOptions)) { + return; + } + + for (AbstractFormOption showOption : showOptions) { + addShowOptionAfter(showOption, allFormOptions, newFormOptions); + } + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/PluginDiscoveryUtil.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/PluginDiscoveryUtil.java new file mode 100644 index 000000000..9ef901437 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/PluginDiscoveryUtil.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.framework; + +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.api.source.SupportColumnProjection; +import org.apache.seatunnel.api.table.factory.Factory; +import org.apache.seatunnel.api.table.factory.FactoryUtil; +import org.apache.seatunnel.api.table.factory.TableSourceFactory; +import org.apache.seatunnel.app.domain.response.connector.ConnectorFeature; +import org.apache.seatunnel.app.domain.response.connector.ConnectorInfo; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.common.config.Common; +import org.apache.seatunnel.common.constants.PluginType; +import org.apache.seatunnel.common.utils.FileUtils; +import org.apache.seatunnel.plugin.discovery.AbstractPluginDiscovery; +import org.apache.seatunnel.plugin.discovery.PluginIdentifier; +import org.apache.seatunnel.plugin.discovery.seatunnel.SeaTunnelSinkPluginDiscovery; + +import lombok.NonNull; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +public class PluginDiscoveryUtil { + + public static List getAllConnectorsFromPluginMapping(PluginType pluginType) { + Map plugins = AbstractPluginDiscovery.getAllSupportedPlugins(pluginType); + List connectorInfos = new ArrayList<>(); + plugins.forEach((plugin, artifactId) -> connectorInfos.add(new ConnectorInfo(plugin, artifactId))); + return connectorInfos; + } + + public static Map getConnectorFeatures(PluginType pluginType) throws IOException { + Common.setStarter(true); + if (!pluginType.equals(PluginType.SOURCE)) { + throw new UnsupportedOperationException("ONLY support plugin type source"); + } + Path path = new SeaTunnelSinkPluginDiscovery().getPluginDir(); + List factories; + if (path.toFile().exists()) { + List files = FileUtils.searchJarFiles(path); + factories = FactoryUtil.discoverFactories(new URLClassLoader(files.toArray(new URL[0]))); + } else { + factories = FactoryUtil.discoverFactories(Thread.currentThread().getContextClassLoader()); + } + Map featureMap = new ConcurrentHashMap<>(); + factories.forEach(plugin -> { + if (TableSourceFactory.class.isAssignableFrom(plugin.getClass())) { + TableSourceFactory tableSourceFactory = (TableSourceFactory) plugin; + PluginIdentifier info = PluginIdentifier.of("seatunnel", PluginType.SOURCE.getType(), plugin.factoryIdentifier()); + featureMap.put(info, new ConnectorFeature(SupportColumnProjection.class.isAssignableFrom(tableSourceFactory.getSourceClass()))); + } + }); + return featureMap; + } + + public static List getDownloadedConnectors(@NonNull Map> allPlugins, @NonNull PluginType pluginType) throws IOException { + LinkedHashMap pluginIdentifierOptionRuleLinkedHashMap = allPlugins.get(pluginType); + if (pluginIdentifierOptionRuleLinkedHashMap == null) { + return new ArrayList<>(); + } + + List connectorInfos = new ArrayList<>(); + pluginIdentifierOptionRuleLinkedHashMap.forEach((plugin, optionRule) -> connectorInfos.add(new ConnectorInfo(plugin, null))); + return connectorInfos; + } + + public static List getTransforms(@NonNull Map> allPlugins) throws IOException { + LinkedHashMap pluginIdentifierOptionRuleLinkedHashMap = allPlugins.get(PluginType.TRANSFORM); + if (pluginIdentifierOptionRuleLinkedHashMap == null) { + return new ArrayList<>(); + } + return pluginIdentifierOptionRuleLinkedHashMap.keySet().stream().map(t -> new ConnectorInfo(t, null)).collect(Collectors.toList()); + } + + public static Map> getAllConnectors() throws IOException { + return new SeaTunnelSinkPluginDiscovery().getAllPlugin(); + } + + public static ConcurrentMap getDownloadedConnectorFormStructures(@NonNull Map> allPlugins, @NonNull PluginType pluginType) { + LinkedHashMap pluginIdentifierOptionRuleLinkedHashMap = allPlugins.get(pluginType); + ConcurrentMap result = new ConcurrentHashMap<>(); + if (pluginIdentifierOptionRuleLinkedHashMap == null) { + return result; + } + + pluginIdentifierOptionRuleLinkedHashMap.forEach((key, value) -> result.put(key.getPluginName(), SeaTunnelOptionRuleWrapper.wrapper(value, key.getPluginName(), pluginType))); + + return result; + } + + public static ConcurrentMap getTransformFormStructures(Map> allPlugins) { + return getDownloadedConnectorFormStructures(allPlugins, PluginType.TRANSFORM); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/SeaTunnelOptionRuleWrapper.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/SeaTunnelOptionRuleWrapper.java new file mode 100644 index 000000000..d54f3a4ec --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/SeaTunnelOptionRuleWrapper.java @@ -0,0 +1,484 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.framework; + +import static org.apache.seatunnel.app.common.SeaTunnelConnectorI18n.CONNECTOR_I18N_CONFIG_EN; +import static org.apache.seatunnel.app.common.SeaTunnelConnectorI18n.CONNECTOR_I18N_CONFIG_ZH; + +import org.apache.seatunnel.api.configuration.Option; +import org.apache.seatunnel.api.configuration.SingleChoiceOption; +import org.apache.seatunnel.api.configuration.util.Expression; +import org.apache.seatunnel.api.configuration.util.OptionRule; +import org.apache.seatunnel.api.configuration.util.RequiredOption; +import org.apache.seatunnel.app.dynamicforms.AbstractFormOption; +import org.apache.seatunnel.app.dynamicforms.FormLocale; +import org.apache.seatunnel.app.dynamicforms.FormOptionBuilder; +import org.apache.seatunnel.app.dynamicforms.FormStructure; +import org.apache.seatunnel.app.dynamicforms.FormStructureBuilder; +import org.apache.seatunnel.app.dynamicforms.validate.ValidateBuilder; +import org.apache.seatunnel.common.constants.PluginType; + +import lombok.NonNull; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class SeaTunnelOptionRuleWrapper { + public static FormStructure wrapper( + @NonNull OptionRule optionRule, + @NonNull String connectorName, + @NonNull PluginType pluginType) { + return wrapper( + optionRule.getOptionalOptions(), + optionRule.getRequiredOptions(), + connectorName + "[" + pluginType.getType() + "]"); + } + + public static FormStructure wrapper( + @NonNull List> optionList, + @NonNull List requiredList, + @NonNull String connectorName, + @NonNull PluginType pluginType) { + return wrapper(optionList, requiredList, connectorName + "[" + pluginType.getType() + "]"); + } + + public static FormStructure wrapper(@NonNull OptionRule optionRule, @NonNull String name) { + return wrapper(optionRule.getOptionalOptions(), optionRule.getRequiredOptions(), name); + } + + public static FormStructure wrapper( + @NonNull List> optionList, + @NonNull List requiredList, + @NonNull String name) { + FormLocale locale = new FormLocale(); + List optionFormOptions = wrapperOptionOptions(name, optionList, locale); + List> requiredFormOptions = + wrapperRequiredOptions(name, requiredList, locale); + + FormStructureBuilder formStructureBuilder = FormStructure.builder().name(name); + + if (!CollectionUtils.isEmpty(requiredFormOptions)) { + requiredFormOptions.forEach( + list -> { + if (CollectionUtils.isEmpty(list)) { + return; + } + + formStructureBuilder.addFormOption(list.toArray(new AbstractFormOption[1])); + }); + } + + if (!CollectionUtils.isEmpty(optionFormOptions)) { + formStructureBuilder.addFormOption( + optionFormOptions.toArray(new AbstractFormOption[1])); + } + + formStructureBuilder.withLocale(locale); + + return FormOptionSort.sortFormStructure(formStructureBuilder.build()); + } + + private static List wrapperOptionOptions( + @NonNull String connectorName, @NonNull List> optionList, FormLocale locale) { + return optionList.stream() + .map( + option -> { + return wrapperToFormOption(connectorName, option, locale); + }) + .collect(Collectors.toList()); + } + + private static List> wrapperRequiredOptions( + @NonNull String connectorName, + @NonNull List requiredList, + FormLocale locale) { + List> formOptionsList = + requiredList.stream() + .map( + option -> { + if (option instanceof RequiredOption.AbsolutelyRequiredOptions) { + List collect = + ((RequiredOption.AbsolutelyRequiredOptions) option) + .getRequiredOption().stream() + .map( + requiredOption -> { + return wrapperToFormOption( + connectorName, + requiredOption, + locale) + .withValidate( + ValidateBuilder + .builder() + .nonEmptyValidateBuilder() + .nonEmptyValidate()); + }) + .collect(Collectors.toList()); + return collect; + } + + if (option instanceof RequiredOption.BundledRequiredOptions) { + List> bundledRequiredOptions = + ((RequiredOption.BundledRequiredOptions) option) + .getRequiredOption(); + List bundledFields = + bundledRequiredOptions.stream() + .map(requiredOption -> requiredOption.key()) + .collect(Collectors.toList()); + + List collect = + bundledRequiredOptions.stream() + .map( + requiredOption -> { + AbstractFormOption + bundledRequiredFormOption = + wrapperToFormOption( + connectorName, + requiredOption, + locale); + bundledRequiredFormOption + .withValidate( + ValidateBuilder + .builder() + .unionNonEmptyValidateBuilder() + .fields( + bundledFields + .toArray( + new String + [1])) + .unionNonEmptyValidate()); + return bundledRequiredFormOption; + }) + .collect(Collectors.toList()); + return collect; + } + + if (option instanceof RequiredOption.ExclusiveRequiredOptions) { + List> exclusiveOptions = + ((RequiredOption.ExclusiveRequiredOptions) option) + .getExclusiveOptions(); + List exclusiveFields = + exclusiveOptions.stream() + .map(requiredOption -> requiredOption.key()) + .collect(Collectors.toList()); + + List collect = + exclusiveOptions.stream() + .map( + requiredOption -> { + AbstractFormOption + exclusiveRequiredFormOption = + wrapperToFormOption( + connectorName, + requiredOption, + locale) + .withValidate( + ValidateBuilder + .builder() + .mutuallyExclusiveValidateBuilder() + .fields( + exclusiveFields + .toArray( + new String + [1])) + .mutuallyExclusiveValidate()); + return exclusiveRequiredFormOption; + }) + .collect(Collectors.toList()); + return collect; + } + + if (option instanceof RequiredOption.ConditionalRequiredOptions) { + RequiredOption.ConditionalRequiredOptions + conditionalRequiredOptions = + (RequiredOption.ConditionalRequiredOptions) + option; + + // we only support one field to control a form option, so we + // only need get condition key from the + // first expression. And all expression is 'or' and every + // condition have the same key. + String conditionKey = + conditionalRequiredOptions + .getExpression() + .getCondition() + .getOption() + .key(); + List expectValueList = new ArrayList<>(); + Expression expression = + conditionalRequiredOptions.getExpression(); + expectValueList.add( + expression + .getCondition() + .getExpectValue() + .toString()); + while (expression.hasNext()) { + expression = expression.getNext(); + expectValueList.add( + expression + .getCondition() + .getExpectValue() + .toString()); + } + List collect = + conditionalRequiredOptions.getRequiredOption() + .stream() + .map( + requiredOption -> { + return wrapperToFormOption( + connectorName, + requiredOption, + locale) + .withShow( + conditionKey, + expectValueList) + .withValidate( + ValidateBuilder + .builder() + .nonEmptyValidateBuilder() + .nonEmptyValidate()); + }) + .collect(Collectors.toList()); + return collect; + } + + throw new UnSupportWrapperException( + connectorName, "Unknown", option.toString()); + }) + .collect(Collectors.toList()); + + return formOptionsList; + } + + private static AbstractFormOption wrapperToFormOption( + @NonNull String connectorName, @NonNull Option option, @NonNull FormLocale locale) { + if (Boolean.class.equals(option.typeReference().getType())) { + return selectInput( + connectorName, + option, + Arrays.asList( + new ImmutablePair("true", true), new ImmutablePair("false", false)), + locale); + } + + if (Double.class.equals(option.typeReference().getType()) + || Duration.class.equals(option.typeReference().getType()) + || Float.class.equals(option.typeReference().getType()) + || Integer.class.equals(option.typeReference().getType()) + || Long.class.equals(option.typeReference().getType()) + || String.class.equals(option.typeReference().getType())) { + + if (option.key().toLowerCase(Locale.ROOT).equals("password")) { + return passwordInput(connectorName, option, locale); + } + if (option.defaultValue() != null && option.defaultValue().toString().contains("\n")) { + return textareaInput(connectorName, option, locale); + } + + return textInput(connectorName, option, locale); + } + + if (option.typeReference().getType().getTypeName().startsWith("java.util.List") + || option.typeReference().getType().getTypeName().startsWith("java.util.Map") + || option.typeReference() + .getType() + .getTypeName() + .startsWith("org.apache.seatunnel.api.configuration.Options")) { + + return textareaInput(connectorName, option, locale); + } + + if (SingleChoiceOption.class.isAssignableFrom(option.getClass())) { + List optionValues = ((SingleChoiceOption) option).getOptionValues(); + List staticSelectOptions = + optionValues.stream() + .map(o -> new ImmutablePair(o.toString(), o.toString())) + .collect(Collectors.toList()); + return selectInput(connectorName, option, staticSelectOptions, locale); + } + + if (((Class) option.typeReference().getType()).isEnum()) { + Object[] enumConstants = ((Class) option.typeReference().getType()).getEnumConstants(); + List staticSelectOptions = + Arrays.stream(enumConstants) + .map(o -> new ImmutablePair(o.toString(), o.toString())) + .collect(Collectors.toList()); + return selectInput(connectorName, option, staticSelectOptions, locale); + } + + if (((Class) option.typeReference().getType()) + .getTypeName() + .startsWith("org.apache.seatunnel")) { + return textareaInput(connectorName, option, locale); + } + + // object type and map type will show as textarea + throw new UnSupportWrapperException( + connectorName, option.key(), option.typeReference().getType().getTypeName()); + } + + private static boolean enableLabelI18n( + String connectorName, String optionI18nKey, FormLocale locale) { + String realConnectorName = connectorName; + if (connectorName.contains("[")) { + realConnectorName = connectorName.substring(0, connectorName.indexOf("[")); + } + + boolean labelEnableI18n = false; + if (CONNECTOR_I18N_CONFIG_ZH.hasPath(realConnectorName) + && CONNECTOR_I18N_CONFIG_ZH.getConfig(realConnectorName).hasPath(optionI18nKey)) { + locale.addZhCN( + optionI18nKey, + CONNECTOR_I18N_CONFIG_ZH.getConfig(realConnectorName).getString(optionI18nKey)); + labelEnableI18n = true; + } + + if (CONNECTOR_I18N_CONFIG_EN.hasPath(realConnectorName) + && CONNECTOR_I18N_CONFIG_EN.getConfig(realConnectorName).hasPath(optionI18nKey)) { + locale.addEnUS( + optionI18nKey, + CONNECTOR_I18N_CONFIG_EN.getConfig(realConnectorName).getString(optionI18nKey)); + labelEnableI18n = true; + } + return labelEnableI18n; + } + + private static AbstractFormOption selectInput( + String connectorName, + @NonNull Option option, + List staticSelectOptions, + FormLocale locale) { + FormOptionBuilder builder = FormOptionBuilder.builder(); + String i18nOptionKey = option.key().replace(".", "_").replace("-", "_"); + if (enableLabelI18n(connectorName, i18nOptionKey, locale)) { + builder = builder.withI18nLabel(i18nOptionKey); + } else { + builder = builder.withLabel(option.key()); + } + + FormOptionBuilder.StaticSelectOptionBuilder staticSelectOptionBuilder = + builder.withField(option.key()).staticSelectOptionBuilder(); + + for (ImmutablePair selectOption : staticSelectOptions) { + if (enableLabelI18n(connectorName, selectOption.getLeft().toString(), locale)) { + staticSelectOptionBuilder.addI18nSelectOptions(selectOption); + } else { + staticSelectOptionBuilder.addSelectOptions(selectOption); + } + } + + AbstractFormOption abstractFormOption = + staticSelectOptionBuilder + .formStaticSelectOption() + .withDefaultValue( + option.defaultValue() == null ? null + : option.defaultValue().toString()); + + String placeholderI18nOptionKey = i18nOptionKey + "_description"; + if (enableLabelI18n(connectorName, placeholderI18nOptionKey, locale)) { + abstractFormOption = abstractFormOption.withI18nPlaceholder(placeholderI18nOptionKey); + } else { + abstractFormOption = abstractFormOption.withPlaceholder(option.getDescription()); + } + + return abstractFormOption; + } + + private static AbstractFormOption passwordInput( + String connectorName, @NonNull Option option, FormLocale locale) { + FormOptionBuilder builder = FormOptionBuilder.builder(); + String i18nOptionKey = option.key().replace(".", "_").replace("-", "_"); + if (enableLabelI18n(connectorName, i18nOptionKey, locale)) { + builder = builder.withI18nLabel(i18nOptionKey); + } else { + builder = builder.withLabel(option.key()); + } + + AbstractFormOption abstractFormOption = + builder.withField(option.key()) + .inputOptionBuilder() + .formPasswordInputOption() + .withDefaultValue(option.defaultValue()); + + String placeholderI18nOptionKey = i18nOptionKey + "_description"; + if (enableLabelI18n(connectorName, placeholderI18nOptionKey, locale)) { + abstractFormOption = abstractFormOption.withI18nPlaceholder(placeholderI18nOptionKey); + } else { + abstractFormOption = abstractFormOption.withPlaceholder(option.getDescription()); + } + + return abstractFormOption; + } + + private static AbstractFormOption textInput( + String connectorName, @NonNull Option option, FormLocale locale) { + FormOptionBuilder builder = FormOptionBuilder.builder(); + String i18nOptionKey = option.key().replace(".", "_").replace("-", "_"); + String placeholderI18nOptionKey = i18nOptionKey + "_description"; + if (enableLabelI18n(connectorName, i18nOptionKey, locale)) { + builder = builder.withI18nLabel(i18nOptionKey); + } else { + builder = builder.withLabel(option.key()); + } + + AbstractFormOption abstractFormOption = + builder.withField(option.key()) + .inputOptionBuilder() + .formTextInputOption() + .withDefaultValue(option.defaultValue()); + if (enableLabelI18n(connectorName, placeholderI18nOptionKey, locale)) { + abstractFormOption = abstractFormOption.withI18nPlaceholder(placeholderI18nOptionKey); + } else { + abstractFormOption = abstractFormOption.withPlaceholder(option.getDescription()); + } + + return abstractFormOption; + } + + private static AbstractFormOption textareaInput( + String connectorName, @NonNull Option option, FormLocale locale) { + FormOptionBuilder builder = FormOptionBuilder.builder(); + String i18nOptionKey = option.key().replace(".", "_").replace("-", "_"); + String placeholderI18nOptionKey = i18nOptionKey + "_description"; + + if (enableLabelI18n(connectorName, i18nOptionKey, locale)) { + builder = builder.withI18nLabel(i18nOptionKey); + } else { + builder = builder.withLabel(option.key()); + } + + AbstractFormOption abstractFormOption = + builder.withField(option.key()) + .inputOptionBuilder() + .formTextareaInputOption() + .withClearable() + .withDefaultValue(option.defaultValue()); + if (enableLabelI18n(connectorName, placeholderI18nOptionKey, locale)) { + abstractFormOption = abstractFormOption.withI18nPlaceholder(placeholderI18nOptionKey); + } else { + abstractFormOption = abstractFormOption.withPlaceholder(option.getDescription()); + } + + return abstractFormOption; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/UnSupportWrapperException.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/UnSupportWrapperException.java new file mode 100644 index 000000000..8db5be03d --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thridparty/framework/UnSupportWrapperException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.thridparty.framework; + +import lombok.NonNull; + +public class UnSupportWrapperException extends RuntimeException { + public UnSupportWrapperException( + @NonNull String formName, @NonNull String label, @NonNull String typeName) { + super( + String.format( + "Form: %s, label: %s, typeName: %s not yet supported", + formName, label, typeName)); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/utils/JdbcUtils.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/utils/JdbcUtils.java new file mode 100644 index 000000000..56e1af595 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/utils/JdbcUtils.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.seatunnel.app.utils; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.regex.Pattern; + +public class JdbcUtils { + + /** + * Get the host from the jdbc url. + * + * @param url jdbc:clickhouse://localhost:8123 + * @return localhost:8123 + */ + public static String getAddressFromUrl(String url) { + checkNotNull(url, "url can not be null"); + Pattern pattern = Pattern.compile("jdbc:clickhouse:\\/\\/(.*):([0-9]+)(.*)"); + return pattern.matcher(url).replaceAll("$1:$2"); + } + + public static String replaceDatabase(String jdbcUrl, String databaseName) { + if (databaseName == null) { + return jdbcUrl; + } + String[] split = jdbcUrl.split("\\?"); + if (split.length == 1) { + return replaceDatabaseWithoutParameter(jdbcUrl, databaseName); + } + return replaceDatabaseWithoutParameter(split[0], databaseName) + "?" + split[1]; + } + + private static String replaceDatabaseWithoutParameter(String jdbcUrl, String databaseName) { + int lastIndex = jdbcUrl.lastIndexOf(':'); + char[] chars = jdbcUrl.toCharArray(); + for (int i = lastIndex + 1; i < chars.length; i++) { + if (chars[i] == '/') { + return jdbcUrl.substring(0, i + 1) + databaseName; + } + } + return jdbcUrl + "/" + databaseName; + } +} diff --git a/seatunnel-web-dist/release-docs/LICENSE b/seatunnel-web-dist/release-docs/LICENSE index ad7f6d8cf..a4338a4b4 100644 --- a/seatunnel-web-dist/release-docs/LICENSE +++ b/seatunnel-web-dist/release-docs/LICENSE @@ -222,6 +222,7 @@ The text of each license is the standard Apache 2.0 license. (Apache License, Version 2.0) Apache Commons Codec (commons-codec:commons-codec:1.13 - https://commons.apache.org/proper/commons-codec/) (Apache License, Version 2.0) Apache Commons Collections (org.apache.commons:commons-collections4:4.4 - https://commons.apache.org/proper/commons-collections/) (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.12.0 - http://commons.apache.org/proper/commons-lang/) + (Apache License, Version 2.0) Apache Commons IO (commons-io:commons-io:2.11.0 - https://mvnrepository.com/artifact/commons-io/commons-io/2.11.0) (Apache License, Version 2.0) config (com.typesafe:config:1.3.3 - https://github.com/lightbend/config) (The Apache Software License, Version 2.0) Guava: Google Core Libraries for Java (com.google.guava:guava:19.0 - https://github.com/google/guava/guava) (Apache License 2.0) Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:6.2.2.Final - http://hibernate.org/validator/hibernate-validator) diff --git a/seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml b/seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml index a13174fcd..2260084f2 100644 --- a/seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml +++ b/seatunnel-web-dist/src/main/assembly/seatunnel-web-ci.xml @@ -86,6 +86,19 @@ provided + + false + true + false + + *:*:jar + + + org.apache.seatunnel:datasource-*:jar + + /libs + + false diff --git a/seatunnel-web-dist/src/main/assembly/seatunnel-web.xml b/seatunnel-web-dist/src/main/assembly/seatunnel-web.xml index 5cc991a4d..4a726e681 100644 --- a/seatunnel-web-dist/src/main/assembly/seatunnel-web.xml +++ b/seatunnel-web-dist/src/main/assembly/seatunnel-web.xml @@ -86,6 +86,19 @@ provided + + false + true + false + + *:*:jar + + + org.apache.seatunnel:datasource-*:jar + + /libs + + false diff --git a/tools/dependencies/known-dependencies.txt b/tools/dependencies/known-dependencies.txt index 6b69a0f19..0f6896f71 100644 --- a/tools/dependencies/known-dependencies.txt +++ b/tools/dependencies/known-dependencies.txt @@ -4,8 +4,10 @@ classmate-1.3.1.jar commons-collections4-4.4.jar commons-codec-1.13.jar commons-lang3-3.12.0.jar +commons-io-2.11.0.jar config-1.3.3.jar cron-utils-9.1.6.jar +gson-2.8.6.jar guava-19.0.jar hibernate-validator-6.2.2.Final.jar jackson-annotations-2.12.7.jar @@ -53,6 +55,8 @@ seatunnel-common-2.3.1.jar seatunnel-config-base-2.3.1.jar seatunnel-config-shade-2.3.1.jar seatunnel-jackson-2.3.1-optional.jar +seatunnel-api-2.3.1.jar +seatunnel-plugin-discovery-2.3.1.jar slf4j-api-1.7.25.jar snakeyaml-1.29.jar spring-aop-5.3.20.jar From 21a195c824da75ce1049143036661da43ad36ba9 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 17:20:54 +0800 Subject: [PATCH 10/28] [Improve][code style] Add spotless plugin to seatunnel web (#56) --- .github/workflows/backend.yml | 8 +- pom.xml | 241 ++++--- .../datasource/AbstractDataSourceClient.java | 68 +- .../datasource/service/DataSourceService.java | 36 +- .../s3/S3DatasourceChannelTest.java | 29 +- .../datasource/DataSourceClientTest.java | 31 +- .../ElasticSearchDataSourceChannel.java | 45 +- .../elasticsearch/client/EsRestClient.java | 246 ++++---- .../plugin/elasticsearch/client/SSLUtils.java | 40 +- .../ElasticSearchDataSourceChannelTest.java | 66 +- .../ElasticSearchDataSourceFactoryTest.java | 4 +- .../jdbc/ClickhouseDataSourceConfig.java | 42 +- .../jdbc/ClickhouseJdbcDataSourceChannel.java | 55 +- .../clickhouse/jdbc/ClickhouseOptionRule.java | 28 +- .../hive/jdbc/HiveJdbcDataSourceChannel.java | 45 +- .../mysql/jdbc/MysqlDataSourceConfig.java | 24 +- .../jdbc/MysqlJdbcDataSourceChannel.java | 65 +- .../plugin/mysql/jdbc/MysqlOptionRule.java | 28 +- .../oracle/jdbc/OracleDataSourceChannel.java | 53 +- .../oracle/jdbc/OracleDataSourceConfig.java | 28 +- .../plugin/oracle/jdbc/OracleOptionRule.java | 24 +- .../jdbc/PostgresqlDataSourceChannel.java | 59 +- .../jdbc/PostgresqlDataSourceConfig.java | 46 +- .../postgresql/jdbc/PostgresqlOptionRule.java | 28 +- .../jdbc/RedshiftDataSourceChannel.java | 57 +- .../jdbc/RedshiftDataSourceConfig.java | 46 +- .../redshift/jdbc/RedshiftOptionRule.java | 28 +- .../jdbc/SqlServerDataSourceChannel.java | 53 +- .../jdbc/SqlServerDataSourceConfig.java | 42 +- .../sqlserver/jdbc/SqlServerOptionRule.java | 26 +- .../jdbc/StarRocksDataSourceConfig.java | 28 +- .../jdbc/StarRocksJdbcDataSourceChannel.java | 57 +- .../starrocks/jdbc/StarRocksOptionRule.java | 28 +- .../tidb/jdbc/TidbDataSourceConfig.java | 24 +- .../tidb/jdbc/TidbJdbcDataSourceChannel.java | 65 +- .../plugin/tidb/jdbc/TidbOptionRule.java | 28 +- .../plugin/kafka/KafkaDataSourceChannel.java | 41 +- .../plugin/kafka/KafkaDataSourceFactory.java | 14 +- .../plugin/kafka/KafkaRequestParamsUtils.java | 30 +- .../kafka/KafkaDataSourceChannelTest.java | 37 +- .../kafka/KafkaRequestParamsUtilsTest.java | 49 +- .../cdc/mysql/MysqlCDCDataSourceChannel.java | 53 +- .../plugin/api/DataSourcePluginInfo.java | 14 +- .../plugin/api/common/ParamtersUtils.java | 8 +- .../redshift/s3/HadoopS3AConfiguration.java | 55 +- .../s3/S3RedshiftDataSourceChannel.java | 83 +-- .../s3/S3RedshiftDataSourceFactory.java | 16 +- .../plugin/s3/HadoopS3AConfiguration.java | 41 +- .../plugin/s3/S3DataSourceFactory.java | 14 +- .../plugin/s3/S3DatasourceChannel.java | 31 +- .../SqlServerCDCDataSourceChannel.java | 65 +- .../SqlServerCDCDataSourceFactory.java | 2 +- .../TestSqlServerCDCDataSourceChannel.java | 13 +- .../plugin/starrocks/StarRocksCatalog.java | 67 +- .../starrocks/StarRocksDataSourceChannel.java | 35 +- .../seatunnel-datasource-plugins/pom.xml | 1 - seatunnel-server/pom.xml | 9 +- seatunnel-server/seatunnel-app/pom.xml | 23 +- .../seatunnel/app/SeatunnelApplication.java | 3 +- .../app/adapter/SeatunnelWebAdapter.java | 23 +- .../seatunnel/app/aspect/LogoutAspect.java | 17 +- .../apache/seatunnel/app/aspect/UserId.java | 3 +- .../seatunnel/app/common/RoleTypeEnum.java | 2 +- .../app/common/ScriptParamStatusEnum.java | 2 +- .../app/common/ScriptStatusEnum.java | 2 +- .../seatunnel/app/common/ScriptTypeEnum.java | 2 +- .../app/common/SeaTunnelConnectorI18n.java | 17 +- .../seatunnel/app/common/UserStatusEnum.java | 2 +- .../seatunnel/app/common/UserTypeEnum.java | 2 +- .../apache/seatunnel/app/config/Swagger2.java | 4 +- .../app/controller/AuthController.java | 14 +- .../app/controller/ScriptController.java | 58 +- .../app/controller/TaskController.java | 46 +- .../app/controller/UserController.java | 33 +- .../app/dal/dao/impl/RoleDaoImpl.java | 6 +- .../dal/dao/impl/RoleUserRelationDaoImpl.java | 9 +- .../dal/dao/impl/SchedulerConfigDaoImpl.java | 3 +- .../app/dal/dao/impl/ScriptDaoImpl.java | 25 +- .../dal/dao/impl/ScriptJobApplyDaoImpl.java | 3 +- .../app/dal/dao/impl/ScriptParamDaoImpl.java | 23 +- .../app/dal/dao/impl/UserDaoImpl.java | 20 +- .../dal/mapper/RoleUserRelationMapper.java | 6 +- .../app/dal/mapper/ScriptMapper.java | 14 +- .../app/dal/mapper/UserLoginLogMapper.java | 4 - .../seatunnel/app/dal/mapper/UserMapper.java | 6 +- .../app/domain/request/BasePageReq.java | 1 + .../request/script/CreateScriptReq.java | 15 +- .../request/script/PublishScriptReq.java | 7 +- .../script/UpdateScriptContentReq.java | 11 +- .../request/script/UpdateScriptParamReq.java | 1 + .../app/domain/request/task/ExecuteReq.java | 20 +- .../domain/request/task/RecycleScriptReq.java | 7 +- .../app/domain/request/user/AddUserReq.java | 3 +- .../app/domain/request/user/BaseUserReq.java | 9 +- .../domain/request/user/UpdateUserReq.java | 5 +- .../app/domain/response/PageInfo.java | 3 +- .../response/script/BaseScriptInfoRes.java | 7 + .../response/script/ScriptFullInfoRes.java | 2 +- .../response/script/ScriptParamRes.java | 2 + .../response/script/ScriptSimpleInfoRes.java | 3 +- .../response/task/InstanceSimpleInfoRes.java | 10 + .../response/task/JobSimpleInfoRes.java | 9 + .../domain/response/user/BaseUserInfoRes.java | 6 + .../AuthenticationInterceptor.java | 35 +- .../UserIdMethodArgumentResolver.java | 12 +- .../seatunnel/app/security/JwtUtils.java | 18 +- .../app/service/impl/RoleServiceImpl.java | 38 +- .../app/service/impl/ScriptServiceImpl.java | 110 ++-- .../app/service/impl/TaskServiceImpl.java | 333 +++++----- .../app/service/impl/UserServiceImpl.java | 69 +- .../AbstractDataSourceConfigSwitcher.java | 198 +++--- .../datasource/DataSourceConfigSwitcher.java | 39 +- .../DataSourceConfigSwitcherUtils.java | 74 +-- .../datasource/SchemaGenerator.java | 50 +- .../BaseJdbcDataSourceConfigSwitcher.java | 102 +-- .../ClickhouseDataSourceConfigSwitcher.java | 127 ++-- ...ElasticSearchDataSourceConfigSwitcher.java | 89 ++- .../impl/KafkaDataSourceConfigSwitcher.java | 97 ++- .../MysqlCDCDataSourceConfigSwitcher.java | 133 ++-- .../impl/MysqlDatasourceConfigSwitcher.java | 3 +- .../PostgresCDCDataSourceConfigSwitcher.java | 143 +++-- .../PostgresqlDataSourceConfigSwitcher.java | 9 +- .../RedshiftDataSourceConfigSwitcher.java | 9 +- .../impl/S3DataSourceConfigSwitcher.java | 238 +++---- .../S3RedshiftDataSourceConfigSwitcher.java | 65 +- .../SqlServerCDCDataSourceConfigSwitcher.java | 143 +++-- .../SqlServerDataSourceConfigSwitcher.java | 5 +- .../StarRocksDataSourceConfigSwitcher.java | 79 ++- .../impl/TidbDataSourceConfigSwitcher.java | 59 +- .../thridparty/framework/FormOptionSort.java | 47 +- .../framework/PluginDiscoveryUtil.java | 80 ++- .../framework/SeaTunnelOptionRuleWrapper.java | 492 +++++++-------- .../app/utils/GlobalExceptionHandler.java | 10 +- .../apache/seatunnel/app/utils/JdbcUtils.java | 4 +- .../apache/seatunnel/app/utils/Md5Utils.java | 3 +- .../seatunnel/app/utils/PasswordUtils.java | 2 +- .../seatunnel/app/WebMvcApplicationTest.java | 7 +- .../app/controller/UserControllerTest.java | 41 +- .../seatunnel-dynamicform/pom.xml | 2 +- .../app/dynamicforms/AbstractFormOption.java | 7 +- .../AbstractFormSelectOption.java | 8 +- .../app/dynamicforms/DynamicSelectOption.java | 4 +- .../app/dynamicforms/FormInputOption.java | 8 +- .../app/dynamicforms/FormLocale.java | 7 +- .../app/dynamicforms/FormOptionBuilder.java | 29 +- .../dynamicforms/FormStructureValidate.java | 317 +++++----- .../app/dynamicforms/StaticSelectOption.java | 6 +- .../validate/AbstractValidate.java | 7 +- .../validate/MutuallyExclusiveValidate.java | 4 +- .../validate/UnionNonEmptyValidate.java | 10 +- .../FormStructureBuilderTest.java | 471 +++++++------- seatunnel-server/seatunnel-scheduler/pom.xml | 9 +- .../pom.xml | 9 +- .../IDolphinschedulerService.java | 3 +- .../constants/DolphinschedulerConstants.java | 21 +- .../dto/ListProcessDefinitionDto.java | 3 +- .../dto/ListProcessInstanceDto.java | 3 +- .../dto/ListTaskInstanceDto.java | 3 +- .../dto/ProcessDefinitionDto.java | 7 +- .../dto/ProcessInstanceDto.java | 11 +- .../dolphinscheduler/dto/ResourceDto.java | 4 +- .../dolphinscheduler/dto/SchedulerDto.java | 10 +- .../dolphinscheduler/dto/TaskInstanceDto.java | 10 +- .../enums/ReleaseStateEnum.java | 2 +- .../impl/DolphinschedulerServiceImpl.java | 587 +++++++++++------- .../impl/InstanceServiceImpl.java | 43 +- .../dolphinscheduler/impl/JobServiceImpl.java | 203 +++--- .../dolphinscheduler/utils/HttpUtils.java | 11 +- .../dolphinscheduler/utils/StatusUtils.java | 1 - .../seatunnel-server-common/pom.xml | 9 +- .../seatunnel/server/common/DateUtils.java | 11 +- .../server/common/SeatunnelErrorEnum.java | 43 +- .../server/common/SeatunnelException.java | 4 +- seatunnel-server/seatunnel-spi/pom.xml | 9 +- .../spi/scheduler/dto/ExecuteDto.java | 1 - .../spi/scheduler/enums/ExecuteTypeEnum.java | 4 +- seatunnel-web-dist/pom.xml | 11 +- 177 files changed, 4213 insertions(+), 3654 deletions(-) diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index eea3507e5..201cee7e2 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -49,8 +49,14 @@ jobs: - uses: actions/checkout@v3 with: submodules: true + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: 'temurin' + cache: 'maven' - name: Check code style - run: ./mvnw --batch-mode --quiet --no-snapshot-updates clean checkstyle:check + run: ./mvnw --batch-mode --quiet --no-snapshot-updates clean spotless:check dead-link: if: github.repository == 'apache/incubator-seatunnel-web' diff --git a/pom.xml b/pom.xml index c015a38de..ef0fc19a7 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - 4.0.0 @@ -26,55 +25,17 @@ org.apache.seatunnel seatunnel-web - pom ${revision} + pom SeaTunnel - - Production ready big data processing product based on Apache Spark and Apache Flink. - - - https://github.com/apache/incubator-seatunnel - - - - The Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - scm:git:https://github.com/apache/incubator-seatunnel.git - scm:git:https://github.com/apache/incubator-seatunnel.git - https://github.com/apache/incubator-seatunnel - HEAD - - - - GitHub - https://github.com/apache/incubator-seatunnel/issues - - - - - SeaTunnel Developer List - dev@seatunnel.apache.org - dev-subscribe@seatunnel.apache.org - dev-unsubscribe@seatunnel.apache.org - - - SeaTunnel Commits List - commits@seatunnel.apache.org - commits-subscribe@seatunnel.apache.org - commits-unsubscribe@seatunnel.apache.org - - + Production ready big data processing product based on Apache Spark and Apache Flink. - seatunnel-server - seatunnel-datasource - seatunnel-web-dist + seatunnel-server + seatunnel-datasource + seatunnel-web-dist @@ -143,6 +104,7 @@ 2.3.1 3.1.4 1.11.271 + 2.29.0 @@ -154,24 +116,24 @@ ${seatunnel-framework.version} - jcl-over-slf4j org.slf4j + jcl-over-slf4j - log4j-1.2-api org.apache.logging.log4j + log4j-1.2-api - log4j-api org.apache.logging.log4j + log4j-api - log4j-core org.apache.logging.log4j + log4j-core - log4j-slf4j-impl org.apache.logging.log4j + log4j-slf4j-impl @@ -187,28 +149,28 @@ jackson-dataformat-properties - slf4j-log4j12 org.slf4j + slf4j-log4j12 - log4j-1.2-api org.apache.logging.log4j + log4j-1.2-api - jcl-over-slf4j org.slf4j + jcl-over-slf4j - log4j-api org.apache.logging.log4j + log4j-api - log4j-core org.apache.logging.log4j + log4j-core - log4j-slf4j-impl org.apache.logging.log4j + log4j-slf4j-impl @@ -219,24 +181,24 @@ ${seatunnel-framework.version} - jcl-over-slf4j org.slf4j + jcl-over-slf4j - log4j-1.2-api org.apache.logging.log4j + log4j-1.2-api - log4j-api org.apache.logging.log4j + log4j-api - log4j-core org.apache.logging.log4j + log4j-core - log4j-slf4j-impl org.apache.logging.log4j + log4j-slf4j-impl @@ -260,24 +222,24 @@ ${seatunnel-framework.version} - jcl-over-slf4j org.slf4j + jcl-over-slf4j - log4j-1.2-api org.apache.logging.log4j + log4j-1.2-api - log4j-api org.apache.logging.log4j + log4j-api - log4j-core org.apache.logging.log4j + log4j-core - log4j-slf4j-impl org.apache.logging.log4j + log4j-slf4j-impl @@ -344,8 +306,8 @@ spring-boot-starter-jdbc - spring-boot-autoconfigure org.springframework.boot + spring-boot-autoconfigure @@ -502,8 +464,8 @@ org.apache.seatunnel seatunnel-hadoop3-3.1.4-uber - provided ${hadoop-uber.version} + provided org.apache.avro @@ -535,8 +497,8 @@ ${cron-utils.version} - javassist org.javassist + javassist @@ -593,8 +555,7 @@ ${skipUT} - ${project.build.directory}/jacoco.exec - + ${project.build.directory}/jacoco.exec **/*IT.java @@ -602,7 +563,6 @@ - org.apache.maven.plugins @@ -664,8 +624,7 @@ ${maven-checkstyle-plugin.version} - ${maven.multiModuleProjectDirectory}/tools/checkstyle/checkStyle.xml - + ${maven.multiModuleProjectDirectory}/tools/checkstyle/checkStyle.xml UTF-8 true true @@ -674,28 +633,23 @@ ${project.build.sourceDirectory} ${project.build.testSourceDirectory} - - **/*.properties, + **/*.properties, **/*.sh, **/*.bat, **/*.yml, **/*.yaml, - **/*.xml - - - **/.asf.yaml, - **/.github/** - - - + **/*.xml + **/.asf.yaml, + **/.github/** + validate - process-sources check + process-sources @@ -737,7 +691,6 @@ - org.codehaus.mojo build-helper-maven-plugin @@ -791,6 +744,81 @@ maven-dependency-plugin ${maven-dependency-plugin.version} + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + 1.7 + + + + + + org.apache.seatunnel.shade,org.apache.seatunnel,org.apache,org,,javax,java,\# + + + Remove wildcard imports + import\s+(static)*\s*[^\*\s]+\*;(\r\n|\r|\n) + $1 + + + Block powermock + import\s+org\.powermock\.[^\*\s]*(|\*);(\r\n|\r|\n) + $1 + + + Block jUnit4 imports + import\s+org\.junit\.[^jupiter][^\*\s]*(|\*);(\r\n|\r|\n) + $1 + + + + + UTF-8 + 4 + true + false + true + true + false + false + custom_1 + false + false + + + Leading blank line + project + project + + + + + docs/**/*.md + + + **/.github/**/*.md + + + + + true + + + + + + check + + compile + + + + @@ -837,7 +865,48 @@ org.codehaus.mojo license-maven-plugin + + + com.diffplug.spotless + spotless-maven-plugin + + https://github.com/apache/incubator-seatunnel + + + + The Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + SeaTunnel Developer List + dev-subscribe@seatunnel.apache.org + dev-unsubscribe@seatunnel.apache.org + dev@seatunnel.apache.org + + + SeaTunnel Commits List + commits-subscribe@seatunnel.apache.org + commits-unsubscribe@seatunnel.apache.org + commits@seatunnel.apache.org + + + + + scm:git:https://github.com/apache/incubator-seatunnel.git + scm:git:https://github.com/apache/incubator-seatunnel.git + https://github.com/apache/incubator-seatunnel + HEAD + + + + GitHub + https://github.com/apache/incubator-seatunnel/issues + + diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java index 7cc2783ed..b9e3b4f33 100644 --- a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java @@ -17,8 +17,6 @@ package org.apache.seatunnel.datasource; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.exception.DataSourceSDKException; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; @@ -34,6 +32,8 @@ import java.util.ServiceLoader; import java.util.concurrent.atomic.AtomicInteger; +import static com.google.common.base.Preconditions.checkNotNull; + public abstract class AbstractDataSourceClient implements DataSourceService { private Map supportedDataSourceInfo = new HashMap<>(); @@ -47,23 +47,23 @@ public abstract class AbstractDataSourceClient implements DataSourceService { protected AbstractDataSourceClient() { AtomicInteger dataSourceIndex = new AtomicInteger(); ServiceLoader.load(DataSourceFactory.class) - .forEach( - seaTunnelDataSourceFactory -> { - seaTunnelDataSourceFactory - .supportedDataSources() - .forEach( - dataSourceInfo -> { - supportedDataSourceInfo.put( - dataSourceInfo.getName().toUpperCase(), - dataSourceInfo); - supportedDataSourceIndex.put( - dataSourceInfo.getName().toUpperCase(), - dataSourceIndex.get()); - supportedDataSources.add(dataSourceInfo); - }); - dataSourceChannels.add(seaTunnelDataSourceFactory.createChannel()); - dataSourceIndex.getAndIncrement(); - }); + .forEach( + seaTunnelDataSourceFactory -> { + seaTunnelDataSourceFactory + .supportedDataSources() + .forEach( + dataSourceInfo -> { + supportedDataSourceInfo.put( + dataSourceInfo.getName().toUpperCase(), + dataSourceInfo); + supportedDataSourceIndex.put( + dataSourceInfo.getName().toUpperCase(), + dataSourceIndex.get()); + supportedDataSources.add(dataSourceInfo); + }); + dataSourceChannels.add(seaTunnelDataSourceFactory.createChannel()); + dataSourceIndex.getAndIncrement(); + }); if (supportedDataSourceInfo.isEmpty()) { throw new DataSourceSDKException("No supported data source found"); } @@ -71,9 +71,9 @@ protected AbstractDataSourceClient() { @Override public Boolean checkDataSourceConnectivity( - String pluginName, Map dataSourceParams) { + String pluginName, Map dataSourceParams) { return getDataSourceChannel(pluginName) - .checkDataSourceConnectivity(pluginName, dataSourceParams); + .checkDataSourceConnectivity(pluginName, dataSourceParams); } @Override @@ -86,7 +86,7 @@ protected DataSourceChannel getDataSourceChannel(String pluginName) { Integer index = supportedDataSourceIndex.get(pluginName.toUpperCase()); if (index == null) { throw new DataSourceSDKException( - "The %s plugin is not supported or plugin not exist.", pluginName); + "The %s plugin is not supported or plugin not exist.", pluginName); } return dataSourceChannels.get(index); } @@ -99,12 +99,12 @@ public OptionRule queryDataSourceFieldByName(String pluginName) { @Override public OptionRule queryMetadataFieldByName(String pluginName) { return getDataSourceChannel(pluginName) - .getDatasourceMetadataFieldsByDataSourceName(pluginName); + .getDatasourceMetadataFieldsByDataSourceName(pluginName); } @Override public List getTables( - String pluginName, String databaseName, Map requestParams) { + String pluginName, String databaseName, Map requestParams) { return getDataSourceChannel(pluginName).getTables(pluginName, requestParams, databaseName); } @@ -115,21 +115,21 @@ public List getDatabases(String pluginName, Map requestP @Override public List getTableFields( - String pluginName, - Map requestParams, - String databaseName, - String tableName) { + String pluginName, + Map requestParams, + String databaseName, + String tableName) { return getDataSourceChannel(pluginName) - .getTableFields(pluginName, requestParams, databaseName, tableName); + .getTableFields(pluginName, requestParams, databaseName, tableName); } @Override public Map> getTableFields( - String pluginName, - Map requestParams, - String databaseName, - List tableNames) { + String pluginName, + Map requestParams, + String databaseName, + List tableNames) { return getDataSourceChannel(pluginName) - .getTableFields(pluginName, requestParams, databaseName, tableNames); + .getTableFields(pluginName, requestParams, databaseName, tableNames); } } diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java index 9ab18325d..5e52c0777 100644 --- a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java +++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java @@ -67,18 +67,18 @@ public interface DataSourceService { /** * get data source table names by database name * - * @param pluginName plugin name - * @param databaseName database name + * @param pluginName plugin name + * @param databaseName database name * @param requestParams connection params * @return table names */ List getTables( - String pluginName, String databaseName, Map requestParams); + String pluginName, String databaseName, Map requestParams); /** * get data source database names * - * @param pluginName plugin name + * @param pluginName plugin name * @param requestParams connection params * @return database names */ @@ -87,30 +87,30 @@ List getTables( /** * get data source table fields * - * @param pluginName plugin name + * @param pluginName plugin name * @param requestParams connection params - * @param databaseName database name - * @param tableName table name + * @param databaseName database name + * @param tableName table name * @return table fields */ List getTableFields( - String pluginName, - Map requestParams, - String databaseName, - String tableName); + String pluginName, + Map requestParams, + String databaseName, + String tableName); /** * get data source table fields * - * @param pluginName plugin name + * @param pluginName plugin name * @param requestParams connection params - * @param databaseName database name - * @param tableNames table names + * @param databaseName database name + * @param tableNames table names * @return table fields */ Map> getTableFields( - String pluginName, - Map requestParams, - String databaseName, - List tableNames); + String pluginName, + Map requestParams, + String databaseName, + List tableNames); } diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java index ece18206a..50c3d2d5b 100644 --- a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java +++ b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java @@ -19,11 +19,12 @@ import org.apache.seatunnel.datasource.plugin.s3.S3DatasourceChannel; -import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import com.google.common.collect.ImmutableMap; + import java.util.Map; @Disabled @@ -33,23 +34,23 @@ class S3DatasourceChannelTest { @Test void checkDataSourceConnectivity() { Assertions.assertDoesNotThrow( - () -> { - S3_DATASOURCE_CHANNEL.checkDataSourceConnectivity("S3", createRequestParams()); - }); + () -> { + S3_DATASOURCE_CHANNEL.checkDataSourceConnectivity("S3", createRequestParams()); + }); } private Map createRequestParams() { Map requestParams = - new ImmutableMap.Builder() - .put("bucket", "s3a://poc-kuke") - .put("fs.s3a.endpoint", "s3.cn-north-1.amazonaws.com.cn") - .put( - "fs.s3a.aws.credentials.provider", - "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider") - .put("access_key", "AKIAYYUV5DMXADXRBGTA") - .put("secret_key", "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") - .put("hadoop_s3_properties", "") - .build(); + new ImmutableMap.Builder() + .put("bucket", "s3a://poc-kuke") + .put("fs.s3a.endpoint", "s3.cn-north-1.amazonaws.com.cn") + .put( + "fs.s3a.aws.credentials.provider", + "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider") + .put("access_key", "AKIAYYUV5DMXADXRBGTA") + .put("secret_key", "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") + .put("hadoop_s3_properties", "") + .build(); return requestParams; } } diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java index e2877e88b..1c45fe7e1 100644 --- a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java +++ b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java @@ -18,6 +18,7 @@ package org.apache.seatunnel.datasource; import org.apache.commons.lang3.StringUtils; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -27,22 +28,22 @@ class DataSourceClientTest { @Test public void listAllDataSources() { Assertions.assertTrue( - DATA_SOURCE_CLIENT.listAllDataSources().stream() - .anyMatch( - dataSourcePluginInfo -> - StringUtils.equalsAnyIgnoreCase( - dataSourcePluginInfo.getName(), "jdbc-mysql"))); + DATA_SOURCE_CLIENT.listAllDataSources().stream() + .anyMatch( + dataSourcePluginInfo -> + StringUtils.equalsAnyIgnoreCase( + dataSourcePluginInfo.getName(), "jdbc-mysql"))); Assertions.assertTrue( - DATA_SOURCE_CLIENT.listAllDataSources().stream() - .anyMatch( - dataSourcePluginInfo -> - StringUtils.equalsAnyIgnoreCase( - dataSourcePluginInfo.getName(), "kafka"))); + DATA_SOURCE_CLIENT.listAllDataSources().stream() + .anyMatch( + dataSourcePluginInfo -> + StringUtils.equalsAnyIgnoreCase( + dataSourcePluginInfo.getName(), "kafka"))); Assertions.assertTrue( - DATA_SOURCE_CLIENT.listAllDataSources().stream() - .anyMatch( - dataSourcePluginInfo -> - StringUtils.equalsAnyIgnoreCase( - dataSourcePluginInfo.getName(), "elasticsearch"))); + DATA_SOURCE_CLIENT.listAllDataSources().stream() + .anyMatch( + dataSourcePluginInfo -> + StringUtils.equalsAnyIgnoreCase( + dataSourcePluginInfo.getName(), "elasticsearch"))); } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java index 17495487f..6a4d92436 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java @@ -17,16 +17,17 @@ package org.apache.seatunnel.datasource.plugin.elasticsearch; +import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; + import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.elasticsearch.client.EsRestClient; -import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; +import org.apache.commons.lang3.StringUtils; import lombok.NonNull; -import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.HashMap; @@ -54,47 +55,47 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { databaseCheck(database); try (EsRestClient client = - EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { + EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { return client.listIndex(); } } @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { return DEFAULT_DATABASES; } @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (EsRestClient client = - EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { + EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { client.getClusterInfo(); return true; } catch (Throwable e) { throw new DataSourcePluginException( - "check ElasticSearch connectivity failed, " + e.getMessage(), e); + "check ElasticSearch connectivity failed, " + e.getMessage(), e); } } @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { databaseCheck(database); try (EsRestClient client = - EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { + EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) { Map fieldTypeMapping = client.getFieldTypeMapping(table); List fields = new ArrayList<>(); fieldTypeMapping.forEach( - (fieldName, fieldType) -> - fields.add(convertToTableField(fieldName, fieldType))); + (fieldName, fieldType) -> + fields.add(convertToTableField(fieldName, fieldType))); return fields; } catch (Exception ex) { throw new DataSourcePluginException("Get table fields failed", ex); @@ -103,16 +104,16 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { databaseCheck(database); Map> tableFields = new HashMap<>(); tables.forEach( - table -> - tableFields.put( - table, getTableFields(pluginName, requestParams, database, table))); + table -> + tableFields.put( + table, getTableFields(pluginName, requestParams, database, table))); return tableFields; } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java index 1ef2e7050..4de06bf4a 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java @@ -17,15 +17,14 @@ package org.apache.seatunnel.datasource.plugin.elasticsearch.client; -import org.apache.seatunnel.common.utils.JsonUtils; -import org.apache.seatunnel.datasource.plugin.elasticsearch.ElasticSearchOptionRule; - import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.JsonNode; import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.ObjectMapper; import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.seatunnel.shade.com.typesafe.config.Config; -import lombok.extern.slf4j.Slf4j; +import org.apache.seatunnel.common.utils.JsonUtils; +import org.apache.seatunnel.datasource.plugin.elasticsearch.ElasticSearchOptionRule; + import org.apache.http.HttpHost; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; @@ -36,11 +35,14 @@ import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; + import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; +import lombok.extern.slf4j.Slf4j; + import javax.net.ssl.SSLContext; import java.io.IOException; @@ -69,18 +71,18 @@ private EsRestClient(RestClient restClient) { public static EsRestClient createInstance(Config pluginConfig) { try { List hosts = - OBJECT_MAPPER.readValue( - pluginConfig.getString(ElasticSearchOptionRule.HOSTS.key()), - List.class); + OBJECT_MAPPER.readValue( + pluginConfig.getString(ElasticSearchOptionRule.HOSTS.key()), + List.class); Optional username = Optional.empty(); Optional password = Optional.empty(); if (pluginConfig.hasPath(ElasticSearchOptionRule.USERNAME.key())) { username = - Optional.of(pluginConfig.getString(ElasticSearchOptionRule.USERNAME.key())); + Optional.of(pluginConfig.getString(ElasticSearchOptionRule.USERNAME.key())); if (pluginConfig.hasPath(ElasticSearchOptionRule.PASSWORD.key())) { password = - Optional.of( - pluginConfig.getString(ElasticSearchOptionRule.PASSWORD.key())); + Optional.of( + pluginConfig.getString(ElasticSearchOptionRule.PASSWORD.key())); } } Optional keystorePath = Optional.empty(); @@ -88,141 +90,141 @@ public static EsRestClient createInstance(Config pluginConfig) { Optional truststorePath = Optional.empty(); Optional truststorePassword = Optional.empty(); boolean tlsVerifyCertificate = - ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.defaultValue(); + ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.defaultValue(); if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.key())) { tlsVerifyCertificate = - pluginConfig.getBoolean( - ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.key()); + pluginConfig.getBoolean( + ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.key()); } if (tlsVerifyCertificate) { if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_KEY_STORE_PATH.key())) { keystorePath = - Optional.of( - pluginConfig.getString( - ElasticSearchOptionRule.TLS_KEY_STORE_PATH.key())); + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_KEY_STORE_PATH.key())); } if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_KEY_STORE_PASSWORD.key())) { keystorePassword = - Optional.of( - pluginConfig.getString( - ElasticSearchOptionRule.TLS_KEY_STORE_PASSWORD.key())); + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_KEY_STORE_PASSWORD.key())); } if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_TRUST_STORE_PATH.key())) { truststorePath = - Optional.of( - pluginConfig.getString( - ElasticSearchOptionRule.TLS_TRUST_STORE_PATH.key())); + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_TRUST_STORE_PATH.key())); } if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_TRUST_STORE_PASSWORD.key())) { truststorePassword = - Optional.of( - pluginConfig.getString( - ElasticSearchOptionRule.TLS_TRUST_STORE_PASSWORD - .key())); + Optional.of( + pluginConfig.getString( + ElasticSearchOptionRule.TLS_TRUST_STORE_PASSWORD + .key())); } } boolean tlsVerifyHostnames = ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.defaultValue(); if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.key())) { tlsVerifyHostnames = - pluginConfig.getBoolean(ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.key()); + pluginConfig.getBoolean(ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.key()); } return createInstance( - hosts, - username, - password, - tlsVerifyCertificate, - tlsVerifyHostnames, - keystorePath, - keystorePassword, - truststorePath, - truststorePassword); + hosts, + username, + password, + tlsVerifyCertificate, + tlsVerifyHostnames, + keystorePath, + keystorePassword, + truststorePath, + truststorePassword); } catch (Exception e) { throw new RuntimeException("Create EsRestClient failed", e); } } public static EsRestClient createInstance( - List hosts, - Optional username, - Optional password, - boolean tlsVerifyCertificate, - boolean tlsVerifyHostnames, - Optional keystorePath, - Optional keystorePassword, - Optional truststorePath, - Optional truststorePassword) { + List hosts, + Optional username, + Optional password, + boolean tlsVerifyCertificate, + boolean tlsVerifyHostnames, + Optional keystorePath, + Optional keystorePassword, + Optional truststorePath, + Optional truststorePassword) { RestClientBuilder restClientBuilder = - getRestClientBuilder( - hosts, - username, - password, - tlsVerifyCertificate, - tlsVerifyHostnames, - keystorePath, - keystorePassword, - truststorePath, - truststorePassword); + getRestClientBuilder( + hosts, + username, + password, + tlsVerifyCertificate, + tlsVerifyHostnames, + keystorePath, + keystorePassword, + truststorePath, + truststorePassword); return new EsRestClient(restClientBuilder.build()); } private static RestClientBuilder getRestClientBuilder( - List hosts, - Optional username, - Optional password, - boolean tlsVerifyCertificate, - boolean tlsVerifyHostnames, - Optional keystorePath, - Optional keystorePassword, - Optional truststorePath, - Optional truststorePassword) { + List hosts, + Optional username, + Optional password, + boolean tlsVerifyCertificate, + boolean tlsVerifyHostnames, + Optional keystorePath, + Optional keystorePassword, + Optional truststorePath, + Optional truststorePassword) { HttpHost[] httpHosts = new HttpHost[hosts.size()]; for (int i = 0; i < hosts.size(); i++) { httpHosts[i] = HttpHost.create(hosts.get(i)); } RestClientBuilder restClientBuilder = - RestClient.builder(httpHosts) - .setRequestConfigCallback( - requestConfigBuilder -> - requestConfigBuilder - .setConnectionRequestTimeout( - CONNECTION_REQUEST_TIMEOUT) - .setSocketTimeout(SOCKET_TIMEOUT)); + RestClient.builder(httpHosts) + .setRequestConfigCallback( + requestConfigBuilder -> + requestConfigBuilder + .setConnectionRequestTimeout( + CONNECTION_REQUEST_TIMEOUT) + .setSocketTimeout(SOCKET_TIMEOUT)); restClientBuilder.setHttpClientConfigCallback( - httpClientBuilder -> { - if (username.isPresent()) { - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials( - AuthScope.ANY, - new UsernamePasswordCredentials(username.get(), password.get())); - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - } - - try { - if (tlsVerifyCertificate) { - Optional sslContext = - SSLUtils.buildSSLContext( - keystorePath, - keystorePassword, - truststorePath, - truststorePassword); - sslContext.ifPresent(e -> httpClientBuilder.setSSLContext(e)); - } else { - SSLContext sslContext = - SSLContexts.custom() - .loadTrustMaterial(new TrustAllStrategy()) - .build(); - httpClientBuilder.setSSLContext(sslContext); + httpClientBuilder -> { + if (username.isPresent()) { + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(username.get(), password.get())); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); } - if (!tlsVerifyHostnames) { - httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); + + try { + if (tlsVerifyCertificate) { + Optional sslContext = + SSLUtils.buildSSLContext( + keystorePath, + keystorePassword, + truststorePath, + truststorePassword); + sslContext.ifPresent(e -> httpClientBuilder.setSSLContext(e)); + } else { + SSLContext sslContext = + SSLContexts.custom() + .loadTrustMaterial(new TrustAllStrategy()) + .build(); + httpClientBuilder.setSSLContext(sslContext); + } + if (!tlsVerifyHostnames) { + httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); + } + } catch (Exception e) { + throw new RuntimeException(e); } - } catch (Exception e) { - throw new RuntimeException(e); - } - return httpClientBuilder; - }); + return httpClientBuilder; + }); return restClientBuilder; } @@ -235,12 +237,12 @@ public ElasticsearchClusterInfo getClusterInfo() { JsonNode jsonNode = objectMapper.readTree(result); JsonNode versionNode = jsonNode.get("version"); return ElasticsearchClusterInfo.builder() - .clusterVersion(versionNode.get("number").asText()) - .distribution( - Optional.ofNullable(versionNode.get("distribution")) - .map(JsonNode::asText) - .orElse(null)) - .build(); + .clusterVersion(versionNode.get("number").asText()) + .distribution( + Optional.ofNullable(versionNode.get("distribution")) + .map(JsonNode::asText) + .orElse(null)) + .build(); } catch (IOException e) { throw new ResponseException("fail to get elasticsearch version.", e); } @@ -265,13 +267,13 @@ public List listIndex() { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { String entity = EntityUtils.toString(response.getEntity()); return JsonUtils.toList(entity, Map.class).stream() - .map(map -> map.get("index").toString()) - .collect(Collectors.toList()); + .map(map -> map.get("index").toString()) + .collect(Collectors.toList()); } else { throw new ResponseException( - String.format( - "GET %s response status code=%d", - endpoint, response.getStatusLine().getStatusCode())); + String.format( + "GET %s response status code=%d", + endpoint, response.getStatusLine().getStatusCode())); } } catch (IOException ex) { throw new ResponseException(ex); @@ -289,9 +291,9 @@ public void dropIndex(String tableName) { // todo: if the index doesn't exist, the response status code is 200? if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new ResponseException( - String.format( - "DELETE %s response status code=%d", - endpoint, response.getStatusLine().getStatusCode())); + String.format( + "DELETE %s response status code=%d", + endpoint, response.getStatusLine().getStatusCode())); } } catch (IOException ex) { throw new ResponseException(ex); @@ -315,9 +317,9 @@ public Map getFieldTypeMapping(String index) { } if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new ResponseException( - String.format( - "GET %s response status code=%d", - endpoint, response.getStatusLine().getStatusCode())); + String.format( + "GET %s response status code=%d", + endpoint, response.getStatusLine().getStatusCode())); } String entity = EntityUtils.toString(response.getEntity()); log.info(String.format("GET %s response=%s", endpoint, entity)); @@ -359,9 +361,9 @@ private static Map getFieldTypeMappingFromProperties(JsonNode pr mapping.put(field, type); } else { log.warn( - String.format( - "fail to get elasticsearch field %s mapping type,so give a default type text", - field)); + String.format( + "fail to get elasticsearch field %s mapping type,so give a default type text", + field)); mapping.put(field, "text"); } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java index 9c10cd3b8..8f3caa204 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java @@ -17,8 +17,6 @@ package org.apache.seatunnel.datasource.plugin.elasticsearch.client; -import static java.util.Collections.list; - import io.airlift.security.pem.PemReader; import javax.net.ssl.KeyManager; @@ -43,29 +41,31 @@ import java.util.List; import java.util.Optional; +import static java.util.Collections.list; + @SuppressWarnings("MagicNumber") public final class SSLUtils { public static Optional buildSSLContext( - Optional keyStorePath, - Optional keyStorePassword, - Optional trustStorePath, - Optional trustStorePassword) - throws GeneralSecurityException, IOException { + Optional keyStorePath, + Optional keyStorePassword, + Optional trustStorePath, + Optional trustStorePassword) + throws GeneralSecurityException, IOException { if (!keyStorePath.isPresent() && !trustStorePath.isPresent()) { return Optional.empty(); } return Optional.of( - createSSLContext( - keyStorePath, keyStorePassword, trustStorePath, trustStorePassword)); + createSSLContext( + keyStorePath, keyStorePassword, trustStorePath, trustStorePassword)); } private static SSLContext createSSLContext( - Optional keyStorePath, - Optional keyStorePassword, - Optional trustStorePath, - Optional trustStorePassword) - throws GeneralSecurityException, IOException { + Optional keyStorePath, + Optional keyStorePassword, + Optional trustStorePath, + Optional trustStorePassword) + throws GeneralSecurityException, IOException { // load KeyStore if configured and get KeyManagers KeyStore keyStore = null; KeyManager[] keyManagers = null; @@ -88,7 +88,7 @@ private static SSLContext createSSLContext( } validateCertificates(keyStore); KeyManagerFactory keyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, keyManagerPassword); keyManagers = keyManagerFactory.getKeyManagers(); } @@ -102,14 +102,14 @@ private static SSLContext createSSLContext( // create TrustManagerFactory TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); // get X509TrustManager TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { throw new RuntimeException( - "Unexpected default trust managers:" + Arrays.toString(trustManagers)); + "Unexpected default trust managers:" + Arrays.toString(trustManagers)); } // create SSLContext SSLContext result = SSLContext.getInstance("SSL"); @@ -118,7 +118,7 @@ private static SSLContext createSSLContext( } private static KeyStore loadTrustStore(File trustStorePath, Optional trustStorePassword) - throws IOException, GeneralSecurityException { + throws IOException, GeneralSecurityException { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); try { // attempt to read the trust store as a PEM file @@ -155,10 +155,10 @@ private static void validateCertificates(KeyStore keyStore) throws GeneralSecuri ((X509Certificate) certificate).checkValidity(); } catch (CertificateExpiredException e) { throw new CertificateExpiredException( - "KeyStore certificate is expired: " + e.getMessage()); + "KeyStore certificate is expired: " + e.getMessage()); } catch (CertificateNotYetValidException e) { throw new CertificateNotYetValidException( - "KeyStore certificate is not yet valid: " + e.getMessage()); + "KeyStore certificate is not yet valid: " + e.getMessage()); } } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java index b8b25d841..b888a1ff7 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java @@ -19,14 +19,15 @@ import org.apache.seatunnel.datasource.plugin.api.model.TableField; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + import java.util.List; import java.util.Map; @@ -34,19 +35,19 @@ @Disabled class ElasticSearchDataSourceChannelTest { private static final Logger LOGGER = - LoggerFactory.getLogger(ElasticSearchDataSourceChannelTest.class); + LoggerFactory.getLogger(ElasticSearchDataSourceChannelTest.class); private static final ElasticSearchDataSourceChannel ELASTIC_SEARCH_DATA_SOURCE_CHANNEL = - new ElasticSearchDataSourceChannel(); + new ElasticSearchDataSourceChannel(); private static final String PLUGIN_NAME = "ElasticSearch"; private static final String DATABASE = "Default"; private static final Map REQUEST_MAP = - new ImmutableMap.Builder() - .put(ElasticSearchOptionRule.HOSTS.key(), "[\"http://localhost:9200\"]") - .build(); + new ImmutableMap.Builder() + .put(ElasticSearchOptionRule.HOSTS.key(), "[\"http://localhost:9200\"]") + .build(); @Test void canAbleGetSchema() { @@ -55,60 +56,61 @@ void canAbleGetSchema() { @Test void getDataSourceOptions() { - Assertions.assertNotNull(ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDataSourceOptions(PLUGIN_NAME)); + Assertions.assertNotNull( + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDataSourceOptions(PLUGIN_NAME)); } @Test void getDatasourceMetadataFieldsByDataSourceName() { Assertions.assertNotNull( - ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName( - PLUGIN_NAME)); + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName( + PLUGIN_NAME)); } @Test void getTables() { Assertions.assertDoesNotThrow( - () -> { - List tables = - ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTables( - PLUGIN_NAME, REQUEST_MAP, DATABASE); - LOGGER.info("{}", tables); - }); + () -> { + List tables = + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTables( + PLUGIN_NAME, REQUEST_MAP, DATABASE); + LOGGER.info("{}", tables); + }); } @Test void getDatabases() { Assertions.assertLinesMatch( - Lists.newArrayList("default"), - ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatabases(PLUGIN_NAME, REQUEST_MAP)); + Lists.newArrayList("default"), + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatabases(PLUGIN_NAME, REQUEST_MAP)); } @Test void checkDataSourceConnectivity() { Assertions.assertTrue( - ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity( - PLUGIN_NAME, REQUEST_MAP)); + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity( + PLUGIN_NAME, REQUEST_MAP)); } @Test void getTableFields() { Assertions.assertDoesNotThrow( - () -> { - List tableFields = - ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields( - PLUGIN_NAME, REQUEST_MAP, DATABASE, ""); - LOGGER.info("{}", tableFields); - }); + () -> { + List tableFields = + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields( + PLUGIN_NAME, REQUEST_MAP, DATABASE, ""); + LOGGER.info("{}", tableFields); + }); } @Test void testGetTableFields() { Assertions.assertDoesNotThrow( - () -> { - Map> tableFields = - ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields( - PLUGIN_NAME, REQUEST_MAP, DATABASE, Lists.newArrayList("")); - LOGGER.info("{}", tableFields); - }); + () -> { + Map> tableFields = + ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields( + PLUGIN_NAME, REQUEST_MAP, DATABASE, Lists.newArrayList("")); + LOGGER.info("{}", tableFields); + }); } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java index 85a49e77b..0441d7a12 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java @@ -23,12 +23,12 @@ class ElasticSearchDataSourceFactoryTest { private static final ElasticSearchDataSourceFactory ELASTIC_SEARCH_DATA_SOURCE_FACTORY = - new ElasticSearchDataSourceFactory(); + new ElasticSearchDataSourceFactory(); @Test void factoryIdentifier() { Assertions.assertEquals( - "ElasticSearch", ELASTIC_SEARCH_DATA_SOURCE_FACTORY.factoryIdentifier()); + "ElasticSearch", ELASTIC_SEARCH_DATA_SOURCE_FACTORY.factoryIdentifier()); } @Test diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java index 4862d41d8..4c5b3b2e7 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java @@ -29,31 +29,31 @@ public class ClickhouseDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-ClickHouse"; public static final DataSourcePluginInfo CLICKHOUSE_DATASOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon(PLUGIN_NAME) - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .supportVirtualTables(false) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .supportVirtualTables(false) + .build(); public static final Set CLICKHOUSE_SYSTEM_DATABASES = - Sets.newHashSet( - "system", - "default", - "information_schema", - "mysql", - "performance_schema", - "sys"); + Sets.newHashSet( + "system", + "default", + "information_schema", + "mysql", + "performance_schema", + "sys"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(ClickhouseOptionRule.URL, ClickhouseOptionRule.DRIVER) - .optional(ClickhouseOptionRule.USER, ClickhouseOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(ClickhouseOptionRule.URL, ClickhouseOptionRule.DRIVER) + .optional(ClickhouseOptionRule.USER, ClickhouseOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder() - .required(ClickhouseOptionRule.DATABASE, ClickhouseOptionRule.TABLE) - .build(); + OptionRule.builder() + .required(ClickhouseOptionRule.DATABASE, ClickhouseOptionRule.TABLE) + .build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java index 371411741..ef160afbc 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.clickhouse.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; +import org.apache.commons.lang3.StringUtils; + import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -39,6 +38,8 @@ import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + @Slf4j public class ClickhouseJdbcDataSourceChannel implements DataSourceChannel { @@ -54,13 +55,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); - try (Connection connection = getConnection(requestParams);) { + try (Connection connection = getConnection(requestParams); ) { ResultSet resultSet = - connection - .getMetaData() - .getTables(database, null, null, new String[]{"TABLE"}); + connection + .getMetaData() + .getTables(database, null, null, new String[] {"TABLE"}); while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -75,17 +76,17 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - Statement statement = connection.createStatement(); - ResultSet re = statement.executeQuery("SHOW DATABASES;")) { + Statement statement = connection.createStatement(); + ResultSet re = statement.executeQuery("SHOW DATABASES;")) { // filter system databases while (re.next()) { String dbName = re.getString("name"); if (StringUtils.isNotBlank(dbName) - && !ClickhouseDataSourceConfig.CLICKHOUSE_SYSTEM_DATABASES.contains( - dbName)) { + && !ClickhouseDataSourceConfig.CLICKHOUSE_SYSTEM_DATABASES.contains( + dbName)) { dbNames.add(dbName); } } @@ -97,7 +98,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -107,10 +108,10 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); try (Connection connection = getConnection(requestParams, database)) { DatabaseMetaData metaData = connection.getMetaData(); @@ -139,15 +140,15 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return null; } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -156,17 +157,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(ClickhouseOptionRule.DRIVER.key())); checkNotNull(requestParams.get(ClickhouseOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(ClickhouseOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(ClickhouseOptionRule.URL.key()), databaseName); if (requestParams.containsKey(ClickhouseOptionRule.USER.key())) { String username = requestParams.get(ClickhouseOptionRule.USER.key()); String password = requestParams.get(ClickhouseOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java index 3beff6058..f02e6030d 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java @@ -23,30 +23,30 @@ public class ClickhouseOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription( - "jdbc url, eg:" - + "jdbc:clickhouse://localhost:8123/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:clickhouse://localhost:8123/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .noDefaultValue() - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .noDefaultValue() + .withDescription("driver"); public enum DriverType { ClickHouse("ru.yandex.clickhouse.ClickHouseDriver"); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java index 32543c423..62559037d 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java @@ -22,11 +22,12 @@ import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; @@ -56,13 +57,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { return getTables(pluginName, requestParams, database); } @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try { return getDataBaseNames(pluginName, requestParams); } catch (SQLException e) { @@ -73,25 +74,25 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { return checkJdbcConnectivity(requestParams); } @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { return getTableFields(requestParams, database, table); } @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { Map> tableFields = new HashMap<>(tables.size()); for (String table : tables) { tableFields.put(table, getTableFields(requestParams, database, table)); @@ -104,24 +105,24 @@ protected boolean checkJdbcConnectivity(Map requestParams) { return true; } catch (Exception e) { throw new DataSourcePluginException( - "check jdbc connectivity failed, " + e.getMessage(), e); + "check jdbc connectivity failed, " + e.getMessage(), e); } } protected Connection init(Map requestParams) throws SQLException { if (MapUtils.isEmpty(requestParams)) { throw new DataSourcePluginException( - "Hive jdbc request params is null, please check your config"); + "Hive jdbc request params is null, please check your config"); } String url = requestParams.get(HiveJdbcOptionRule.URL.key()); return DriverManager.getConnection(url); } protected List getDataBaseNames(String pluginName, Map requestParams) - throws SQLException { + throws SQLException { List dbNames = new ArrayList<>(); try (Connection connection = init(requestParams); - Statement statement = connection.createStatement();) { + Statement statement = connection.createStatement(); ) { ResultSet re = statement.executeQuery("SHOW DATABASES;"); // filter system databases while (re.next()) { @@ -136,9 +137,9 @@ protected List getDataBaseNames(String pluginName, Map r protected List getTableNames(Map requestParams, String dbName) { List tableNames = new ArrayList<>(); - try (Connection connection = init(requestParams);) { + try (Connection connection = init(requestParams); ) { ResultSet resultSet = - connection.getMetaData().getTables(dbName, null, null, new String[]{"TABLE"}); + connection.getMetaData().getTables(dbName, null, null, new String[] {"TABLE"}); while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -152,9 +153,9 @@ protected List getTableNames(Map requestParams, String d } protected List getTableFields( - Map requestParams, String dbName, String tableName) { + Map requestParams, String dbName, String tableName) { List tableFields = new ArrayList<>(); - try (Connection connection = init(requestParams);) { + try (Connection connection = init(requestParams); ) { DatabaseMetaData metaData = connection.getMetaData(); String primaryKey = getPrimaryKey(metaData, dbName, tableName); ResultSet resultSet = metaData.getColumns(dbName, null, tableName, null); @@ -180,7 +181,7 @@ protected List getTableFields( } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java index 2e2de3e45..03d6d0cf3 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java @@ -30,22 +30,22 @@ public class MysqlDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-Mysql"; public static final DataSourcePluginInfo MYSQL_DATASOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon(PLUGIN_NAME) - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); public static final Set MYSQL_SYSTEM_DATABASES = - Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys"); + Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(MysqlOptionRule.URL, MysqlOptionRule.DRIVER) - .optional(MysqlOptionRule.USER, MysqlOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(MysqlOptionRule.URL, MysqlOptionRule.DRIVER) + .optional(MysqlOptionRule.USER, MysqlOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder().required(MysqlOptionRule.DATABASE, MysqlOptionRule.TABLE).build(); + OptionRule.builder().required(MysqlOptionRule.DATABASE, MysqlOptionRule.TABLE).build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java index 1fe420d53..99ab042b9 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.mysql.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; -import lombok.NonNull; import org.apache.commons.lang3.StringUtils; +import lombok.NonNull; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; @@ -40,6 +39,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static com.google.common.base.Preconditions.checkNotNull; + public class MysqlJdbcDataSourceChannel implements DataSourceChannel { @Override @@ -54,13 +55,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - ResultSet resultSet = - connection - .getMetaData() - .getTables(database, null, null, new String[]{"TABLE"})) { + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[] {"TABLE"})) { while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -75,16 +76,16 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { // filter system databases while (re.next()) { String dbName = re.getString("database"); if (StringUtils.isNotBlank(dbName) - && !MysqlDataSourceConfig.MYSQL_SYSTEM_DATABASES.contains(dbName)) { + && !MysqlDataSourceConfig.MYSQL_SYSTEM_DATABASES.contains(dbName)) { dbNames.add(dbName); } } @@ -96,7 +97,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -106,10 +107,10 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); try (Connection connection = getConnection(requestParams, database)) { DatabaseMetaData metaData = connection.getMetaData(); @@ -138,21 +139,21 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return tables.parallelStream() - .collect( - Collectors.toMap( - Function.identity(), - table -> - getTableFields( - pluginName, requestParams, database, table))); + .collect( + Collectors.toMap( + Function.identity(), + table -> + getTableFields( + pluginName, requestParams, database, table))); } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -161,17 +162,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(MysqlOptionRule.DRIVER.key())); checkNotNull(requestParams.get(MysqlOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(MysqlOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(MysqlOptionRule.URL.key()), databaseName); if (requestParams.containsKey(MysqlOptionRule.USER.key())) { String username = requestParams.get(MysqlOptionRule.USER.key()); String password = requestParams.get(MysqlOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java index f666251fc..e34696410 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java @@ -23,30 +23,30 @@ public class MysqlOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription( - "jdbc url, eg:" - + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .defaultValue(DriverType.MYSQL) - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.MYSQL) + .withDescription("driver"); public enum DriverType { MYSQL("com.mysql.cj.jdbc.Driver"), diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java index 91ccc6615..87a04fcf6 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.oracle.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; -import lombok.NonNull; import org.apache.commons.lang3.StringUtils; +import lombok.NonNull; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; @@ -38,6 +37,8 @@ import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + public class OracleDataSourceChannel implements DataSourceChannel { @Override @@ -52,13 +53,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - ResultSet resultSet = - connection - .getMetaData() - .getTables(database, null, null, new String[]{"TABLE"});) { + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[] {"TABLE"}); ) { while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -73,16 +74,16 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { // filter system databases while (re.next()) { String dbName = re.getString("database"); if (StringUtils.isNotBlank(dbName) - && !OracleDataSourceConfig.ORACLE_SYSTEM_DATABASES.contains(dbName)) { + && !OracleDataSourceConfig.ORACLE_SYSTEM_DATABASES.contains(dbName)) { dbNames.add(dbName); } } @@ -94,7 +95,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -104,10 +105,10 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); try (Connection connection = getConnection(requestParams, database)) { DatabaseMetaData metaData = connection.getMetaData(); @@ -136,15 +137,15 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return null; } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -153,17 +154,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(OracleOptionRule.DRIVER.key())); checkNotNull(requestParams.get(OracleOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(OracleOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(OracleOptionRule.URL.key()), databaseName); if (requestParams.containsKey(OracleOptionRule.USER.key())) { String username = requestParams.get(OracleOptionRule.USER.key()); String password = requestParams.get(OracleOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java index 96f09fbef..83455b6d7 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java @@ -30,24 +30,24 @@ public class OracleDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-Oracle"; public static final DataSourcePluginInfo ORACLE_DATASOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon(PLUGIN_NAME) - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); public static final Set ORACLE_SYSTEM_DATABASES = - Sets.newHashSet("SYS", "SYSTEM", "SYSDBA", "SYSOPER", "HR", "SCOTT"); + Sets.newHashSet("SYS", "SYSTEM", "SYSDBA", "SYSOPER", "HR", "SCOTT"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(OracleOptionRule.URL, OracleOptionRule.DRIVER) - .optional(OracleOptionRule.USER, OracleOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(OracleOptionRule.URL, OracleOptionRule.DRIVER) + .optional(OracleOptionRule.USER, OracleOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder() - .required(OracleOptionRule.DATABASE, OracleOptionRule.TABLE) - .build(); + OptionRule.builder() + .required(OracleOptionRule.DATABASE, OracleOptionRule.TABLE) + .build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java index 517a8c32e..f3ec40e33 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java @@ -23,28 +23,28 @@ public class OracleOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription("jdbc url, eg:" + "jdbc:oracle:thin:@localhost:1521:XE"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription("jdbc url, eg:" + "jdbc:oracle:thin:@localhost:1521:XE"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .defaultValue(DriverType.ORACLE) - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.ORACLE) + .withDescription("driver"); public enum DriverType { ORACLE("oracle.jdbc.driver.OracleDriver"), diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java index b331d61d0..e43939536 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.postgresql.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; -import lombok.NonNull; import org.apache.commons.lang3.StringUtils; +import lombok.NonNull; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; @@ -39,6 +38,8 @@ import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + public class PostgresqlDataSourceChannel implements DataSourceChannel { @Override @@ -53,18 +54,18 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); String query = "SELECT table_schema, table_name FROM information_schema.tables"; try (Connection connection = getConnection(requestParams, database)) { try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery(query)) { + ResultSet resultSet = statement.executeQuery(query)) { while (resultSet.next()) { String schemaName = resultSet.getString("table_schema"); String tableName = resultSet.getString("table_name"); if (StringUtils.isNotBlank(schemaName) - && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains( - schemaName)) { + && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains( + schemaName)) { tableNames.add(schemaName + "." + tableName); } } @@ -77,17 +78,17 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - PreparedStatement statement = - connection.prepareStatement("select datname from pg_database;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = + connection.prepareStatement("select datname from pg_database;"); + ResultSet re = statement.executeQuery()) { while (re.next()) { String dbName = re.getString("datname"); if (StringUtils.isNotBlank(dbName) - && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains( - dbName)) { + && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains( + dbName)) { dbNames.add(dbName); } } @@ -99,7 +100,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -109,18 +110,18 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); - try (Connection connection = getConnection(requestParams, database);) { + try (Connection connection = getConnection(requestParams, database); ) { DatabaseMetaData metaData = connection.getMetaData(); String primaryKey = getPrimaryKey(metaData, database, table); String[] split = table.split("\\."); if (split.length != 2) { throw new DataSourcePluginException( - "Postgresql tableName should composed by schemaName.tableName"); + "Postgresql tableName should composed by schemaName.tableName"); } try (ResultSet resultSet = metaData.getColumns(database, split[0], split[1], null)) { while (resultSet.next()) { @@ -146,15 +147,15 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return null; } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -163,17 +164,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(PostgresqlOptionRule.DRIVER.key())); checkNotNull(requestParams.get(PostgresqlOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(PostgresqlOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(PostgresqlOptionRule.URL.key()), databaseName); if (requestParams.containsKey(PostgresqlOptionRule.USER.key())) { String username = requestParams.get(PostgresqlOptionRule.USER.key()); String password = requestParams.get(PostgresqlOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java index 2aa0538b2..f150ab5d0 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java @@ -30,33 +30,33 @@ public class PostgresqlDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-Postgres"; public static final DataSourcePluginInfo POSTGRESQL_DATASOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon(PLUGIN_NAME) - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); public static final Set POSTGRESQL_SYSTEM_DATABASES = - Sets.newHashSet( - "information_schema", - "pg_catalog", - "root", - "pg_toast", - "pg_temp_1", - "pg_toast_temp_1", - "postgres", - "template0", - "template1"); + Sets.newHashSet( + "information_schema", + "pg_catalog", + "root", + "pg_toast", + "pg_temp_1", + "pg_toast_temp_1", + "postgres", + "template0", + "template1"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(PostgresqlOptionRule.URL, PostgresqlOptionRule.DRIVER) - .optional(PostgresqlOptionRule.USER, PostgresqlOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(PostgresqlOptionRule.URL, PostgresqlOptionRule.DRIVER) + .optional(PostgresqlOptionRule.USER, PostgresqlOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder() - .required(PostgresqlOptionRule.DATABASE, PostgresqlOptionRule.TABLE) - .build(); + OptionRule.builder() + .required(PostgresqlOptionRule.DATABASE, PostgresqlOptionRule.TABLE) + .build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java index 16e8b85b2..748c4ea79 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java @@ -23,30 +23,30 @@ public class PostgresqlOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription( - "jdbc url, eg:" - + "jdbc:postgresql://localhost:5432//test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:postgresql://localhost:5432//test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .defaultValue(DriverType.POSTGRESQL) - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.POSTGRESQL) + .withDescription("driver"); public enum DriverType { POSTGRESQL("org.postgresql.Driver"), diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java index 32f77e985..0e3dfa709 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.redshift.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; -import lombok.NonNull; import org.apache.commons.lang3.StringUtils; +import lombok.NonNull; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; @@ -38,6 +37,8 @@ import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + public class RedshiftDataSourceChannel implements DataSourceChannel { @Override @@ -52,17 +53,17 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams, database); - ResultSet resultSet = - connection.getMetaData().getTables(database, null, null, null);) { + ResultSet resultSet = + connection.getMetaData().getTables(database, null, null, null); ) { while (resultSet.next()) { String schemaName = resultSet.getString("TABLE_SCHEM"); String tableName = resultSet.getString("TABLE_NAME"); // todo: use isNotSystemSchemaName if (StringUtils.isNotBlank(schemaName) - && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(schemaName)) { + && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(schemaName)) { tableNames.add(schemaName + "." + tableName); } } @@ -74,16 +75,16 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - PreparedStatement statement = - connection.prepareStatement("select datname from pg_database;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = + connection.prepareStatement("select datname from pg_database;"); + ResultSet re = statement.executeQuery()) { while (re.next()) { String dbName = re.getString("datname"); if (StringUtils.isNotBlank(dbName) - && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(dbName)) { + && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(dbName)) { dbNames.add(dbName); } } @@ -95,7 +96,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -105,18 +106,18 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); - try (Connection connection = getConnection(requestParams, database);) { + try (Connection connection = getConnection(requestParams, database); ) { DatabaseMetaData metaData = connection.getMetaData(); String primaryKey = getPrimaryKey(metaData, database, table); String[] split = table.split("\\."); if (split.length != 2) { throw new DataSourcePluginException( - "Postgresql tableName should composed by schemaName.tableName"); + "Postgresql tableName should composed by schemaName.tableName"); } try (ResultSet resultSet = metaData.getColumns(database, split[0], split[1], null)) { while (resultSet.next()) { @@ -142,15 +143,15 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return null; } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -159,17 +160,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(RedshiftOptionRule.DRIVER.key())); checkNotNull(requestParams.get(RedshiftOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(RedshiftOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(RedshiftOptionRule.URL.key()), databaseName); if (requestParams.containsKey(RedshiftOptionRule.USER.key())) { String username = requestParams.get(RedshiftOptionRule.USER.key()); String password = requestParams.get(RedshiftOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java index 450c8d954..b5561f09e 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java @@ -30,33 +30,33 @@ public class RedshiftDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-Redshift"; public static final DataSourcePluginInfo REDSHIFT_DATASOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon("redshift") - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon("redshift") + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); public static final Set REDSHIFT_SYSTEM_TABLES = - Sets.newHashSet( - "information_schema", - "pg_catalog", - "root", - "pg_toast", - "pg_temp_1", - "pg_toast_temp_1", - "postgres", - "template0", - "template1"); + Sets.newHashSet( + "information_schema", + "pg_catalog", + "root", + "pg_toast", + "pg_temp_1", + "pg_toast_temp_1", + "postgres", + "template0", + "template1"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(RedshiftOptionRule.URL, RedshiftOptionRule.DRIVER) - .optional(RedshiftOptionRule.USER, RedshiftOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(RedshiftOptionRule.URL, RedshiftOptionRule.DRIVER) + .optional(RedshiftOptionRule.USER, RedshiftOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder() - .required(RedshiftOptionRule.DATABASE, RedshiftOptionRule.TABLE) - .build(); + OptionRule.builder() + .required(RedshiftOptionRule.DATABASE, RedshiftOptionRule.TABLE) + .build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java index ba4c11c0f..c4f389545 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java @@ -23,30 +23,30 @@ public class RedshiftOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription( - "jdbc url, eg:" - + "jdbc:redshift://server.redshift.amazonaws.com:5439/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:redshift://server.redshift.amazonaws.com:5439/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .defaultValue(DriverType.JDBC42_REDSHIFT) - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.JDBC42_REDSHIFT) + .withDescription("driver"); public enum DriverType { JDBC42_REDSHIFT("com.amazon.redshift.jdbc42.Driver"), diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java index bde7550c9..7a32f86a0 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.sqlserver.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; +import org.apache.commons.lang3.StringUtils; + import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -39,6 +38,8 @@ import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + @Slf4j public class SqlServerDataSourceChannel implements DataSourceChannel { @Override @@ -53,13 +54,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - ResultSet resultSet = - connection - .getMetaData() - .getTables(database, null, null, new String[]{"TABLE"})) { + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[] {"TABLE"})) { while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -74,16 +75,16 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { // filter system databases while (re.next()) { String dbName = re.getString("database"); if (StringUtils.isNotBlank(dbName) - && !SqlServerDataSourceConfig.SQLSERVER_SYSTEM_DATABASES.contains(dbName)) { + && !SqlServerDataSourceConfig.SQLSERVER_SYSTEM_DATABASES.contains(dbName)) { dbNames.add(dbName); } } @@ -95,7 +96,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -105,10 +106,10 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); try (Connection connection = getConnection(requestParams, database)) { DatabaseMetaData metaData = connection.getMetaData(); @@ -137,15 +138,15 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return null; } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -154,17 +155,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(SqlServerOptionRule.DRIVER.key())); checkNotNull(requestParams.get(SqlServerOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(SqlServerOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(SqlServerOptionRule.URL.key()), databaseName); if (requestParams.containsKey(SqlServerOptionRule.USER.key())) { String username = requestParams.get(SqlServerOptionRule.USER.key()); String password = requestParams.get(SqlServerOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java index 3bfc056c1..218464abe 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java @@ -30,31 +30,31 @@ public class SqlServerDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-SQLServer"; public static final DataSourcePluginInfo SQLSERVER_DATASOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon(PLUGIN_NAME) - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); public static final Set SQLSERVER_SYSTEM_DATABASES = - Sets.newHashSet( - "master", - "tempdb", - "model", - "msdb", - "ReportServer", - "ReportServerTempDB", - "SSISDB"); + Sets.newHashSet( + "master", + "tempdb", + "model", + "msdb", + "ReportServer", + "ReportServerTempDB", + "SSISDB"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(SqlServerOptionRule.URL, SqlServerOptionRule.DRIVER) - .optional(SqlServerOptionRule.USER, SqlServerOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(SqlServerOptionRule.URL, SqlServerOptionRule.DRIVER) + .optional(SqlServerOptionRule.USER, SqlServerOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder() - .required(SqlServerOptionRule.DATABASE, SqlServerOptionRule.TABLE) - .build(); + OptionRule.builder() + .required(SqlServerOptionRule.DATABASE, SqlServerOptionRule.TABLE) + .build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java index ec157d39c..6052a7ab1 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java @@ -23,29 +23,29 @@ public class SqlServerOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription( - "jdbc url, eg:" + "jdbc:sqlserver://localhost:1433;database=xx"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + "jdbc:sqlserver://localhost:1433;database=xx"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .defaultValue(DriverType.SQL_SERVER) - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.SQL_SERVER) + .withDescription("driver"); public enum DriverType { SQL_SERVER("com.microsoft.sqlserver.jdbc.SQLServerDriver"), diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java index 427657ccc..913bc5ced 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java @@ -30,24 +30,24 @@ public class StarRocksDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-StarRocks"; public static final DataSourcePluginInfo STAR_ROCKS_DATA_SOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon(PLUGIN_NAME) - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); public static final Set STAR_ROCKS_SYSTEM_DATABASES = - Sets.newHashSet("_statistics_", "information_schema"); + Sets.newHashSet("_statistics_", "information_schema"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(StarRocksOptionRule.URL, StarRocksOptionRule.DRIVER) - .optional(StarRocksOptionRule.USER, StarRocksOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(StarRocksOptionRule.URL, StarRocksOptionRule.DRIVER) + .optional(StarRocksOptionRule.USER, StarRocksOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder() - .required(StarRocksOptionRule.DATABASE, StarRocksOptionRule.TABLE) - .build(); + OptionRule.builder() + .required(StarRocksOptionRule.DATABASE, StarRocksOptionRule.TABLE) + .build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java index 3f429a7cc..65f0f6136 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.starrocks.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; -import lombok.NonNull; import org.apache.commons.lang3.StringUtils; +import lombok.NonNull; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; @@ -38,6 +37,8 @@ import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + public class StarRocksJdbcDataSourceChannel implements DataSourceChannel { @Override @@ -52,13 +53,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - ResultSet resultSet = - connection - .getMetaData() - .getTables(database, null, null, new String[]{"TABLE"})) { + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[] {"TABLE"})) { while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -73,17 +74,17 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { // filter system databases while (re.next()) { String dbName = re.getString("database"); if (StringUtils.isNotBlank(dbName) - && !StarRocksDataSourceConfig.STAR_ROCKS_SYSTEM_DATABASES.contains( - dbName)) { + && !StarRocksDataSourceConfig.STAR_ROCKS_SYSTEM_DATABASES.contains( + dbName)) { dbNames.add(dbName); } } @@ -95,7 +96,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -105,15 +106,15 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); try (Connection connection = getConnection(requestParams, database)) { DatabaseMetaData metaData = connection.getMetaData(); String primaryKey = getPrimaryKey(metaData, database, table); - try (ResultSet resultSet = metaData.getColumns(database, null, table, null);) { + try (ResultSet resultSet = metaData.getColumns(database, null, table, null); ) { while (resultSet.next()) { TableField tableField = new TableField(); String columnName = resultSet.getString("COLUMN_NAME"); @@ -137,15 +138,15 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return null; } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -154,17 +155,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(StarRocksOptionRule.DRIVER.key())); checkNotNull(requestParams.get(StarRocksOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(StarRocksOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(StarRocksOptionRule.URL.key()), databaseName); if (requestParams.containsKey(StarRocksOptionRule.USER.key())) { String username = requestParams.get(StarRocksOptionRule.USER.key()); String password = requestParams.get(StarRocksOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java index d67c2a304..08f2dc21b 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java @@ -23,30 +23,30 @@ public class StarRocksOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription( - "jdbc url, eg:" - + "jdbc:mysql://localhost:9030/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + "jdbc:mysql://localhost:9030/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .defaultValue(DriverType.MYSQL) - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.MYSQL) + .withDescription("driver"); public enum DriverType { MYSQL("com.mysql.cj.jdbc.Driver"), diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java index f02caff2f..c9acb5b6b 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java @@ -30,22 +30,22 @@ public class TidbDataSourceConfig { public static final String PLUGIN_NAME = "JDBC-TiDB"; public static final DataSourcePluginInfo TIDB_DATASOURCE_PLUGIN_INFO = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .icon(PLUGIN_NAME) - .version("1.0.0") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .icon(PLUGIN_NAME) + .version("1.0.0") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .build(); public static final Set TIDB_SYSTEM_DATABASES = - Sets.newHashSet("information_schema", "mysql", "performance_schema", "metrics_schema"); + Sets.newHashSet("information_schema", "mysql", "performance_schema", "metrics_schema"); public static final OptionRule OPTION_RULE = - OptionRule.builder() - .required(TidbOptionRule.URL, TidbOptionRule.DRIVER) - .optional(TidbOptionRule.USER, TidbOptionRule.PASSWORD) - .build(); + OptionRule.builder() + .required(TidbOptionRule.URL, TidbOptionRule.DRIVER) + .optional(TidbOptionRule.USER, TidbOptionRule.PASSWORD) + .build(); public static final OptionRule METADATA_RULE = - OptionRule.builder().required(TidbOptionRule.DATABASE, TidbOptionRule.TABLE).build(); + OptionRule.builder().required(TidbOptionRule.DATABASE, TidbOptionRule.TABLE).build(); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java index 8558f13c0..90688bbb5 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java @@ -17,17 +17,16 @@ package org.apache.seatunnel.datasource.plugin.tidb.jdbc; -import static com.google.common.base.Preconditions.checkNotNull; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; -import lombok.NonNull; import org.apache.commons.lang3.StringUtils; +import lombok.NonNull; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; @@ -40,6 +39,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static com.google.common.base.Preconditions.checkNotNull; + public class TidbJdbcDataSourceChannel implements DataSourceChannel { @Override @@ -54,13 +55,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { List tableNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - ResultSet resultSet = - connection - .getMetaData() - .getTables(database, null, null, new String[]{"TABLE"})) { + ResultSet resultSet = + connection + .getMetaData() + .getTables(database, null, null, new String[] {"TABLE"})) { while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -75,16 +76,16 @@ public List getTables( @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { List dbNames = new ArrayList<>(); try (Connection connection = getConnection(requestParams); - PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { // filter system databases while (re.next()) { String dbName = re.getString("database"); if (StringUtils.isNotBlank(dbName) - && !TidbDataSourceConfig.TIDB_SYSTEM_DATABASES.contains(dbName)) { + && !TidbDataSourceConfig.TIDB_SYSTEM_DATABASES.contains(dbName)) { dbNames.add(dbName); } } @@ -96,7 +97,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (Connection ignored = getConnection(requestParams)) { return true; } catch (Exception e) { @@ -106,10 +107,10 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { List tableFields = new ArrayList<>(); try (Connection connection = getConnection(requestParams, database)) { DatabaseMetaData metaData = connection.getMetaData(); @@ -138,21 +139,21 @@ public List getTableFields( @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { return tables.parallelStream() - .collect( - Collectors.toMap( - Function.identity(), - table -> - getTableFields( - pluginName, requestParams, database, table))); + .collect( + Collectors.toMap( + Function.identity(), + table -> + getTableFields( + pluginName, requestParams, database, table))); } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -161,17 +162,17 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta } private Connection getConnection(Map requestParams) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { return getConnection(requestParams, null); } private Connection getConnection(Map requestParams, String databaseName) - throws SQLException, ClassNotFoundException { + throws SQLException, ClassNotFoundException { checkNotNull(requestParams.get(TidbOptionRule.DRIVER.key())); checkNotNull(requestParams.get(TidbOptionRule.URL.key()), "Jdbc url cannot be null"); String url = - JdbcUtils.replaceDatabase( - requestParams.get(TidbOptionRule.URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(TidbOptionRule.URL.key()), databaseName); if (requestParams.containsKey(TidbOptionRule.USER.key())) { String username = requestParams.get(TidbOptionRule.USER.key()); String password = requestParams.get(TidbOptionRule.PASSWORD.key()); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java index 90af0f95f..7ae009170 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java @@ -23,30 +23,30 @@ public class TidbOptionRule { public static final Option URL = - Options.key("url") - .stringType() - .noDefaultValue() - .withDescription( - "jdbc url, eg:" - + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); + Options.key("url") + .stringType() + .noDefaultValue() + .withDescription( + "jdbc url, eg:" + + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"); public static final Option USER = - Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); + Options.key("user").stringType().noDefaultValue().withDescription("jdbc user"); public static final Option PASSWORD = - Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); + Options.key("password").stringType().noDefaultValue().withDescription("jdbc password"); public static final Option DATABASE = - Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); + Options.key("database").stringType().noDefaultValue().withDescription("jdbc database"); public static final Option TABLE = - Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); + Options.key("table").stringType().noDefaultValue().withDescription("jdbc table"); public static final Option DRIVER = - Options.key("driver") - .enumType(DriverType.class) - .defaultValue(DriverType.MYSQL) - .withDescription("driver"); + Options.key("driver") + .enumType(DriverType.class) + .defaultValue(DriverType.MYSQL) + .withDescription("driver"); public enum DriverType { MYSQL("com.mysql.cj.jdbc.Driver"), diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java index 72e679957..9413bcae1 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java @@ -17,33 +17,34 @@ package org.apache.seatunnel.datasource.plugin.kafka; -import static com.google.common.base.Preconditions.checkArgument; - import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.kafka.clients.admin.AdminClient; import org.apache.kafka.clients.admin.DescribeClusterOptions; import org.apache.kafka.clients.admin.DescribeClusterResult; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import static com.google.common.base.Preconditions.checkArgument; + @Slf4j public class KafkaDataSourceChannel implements DataSourceChannel { private static final String DATABASE = "default"; private static final DescribeClusterOptions DEFAULT_TIMEOUT_OPTIONS = - new DescribeClusterOptions().timeoutMs(60 * 1000); + new DescribeClusterOptions().timeoutMs(60 * 1000); @Override public OptionRule getDataSourceOptions(@NonNull String pluginName) { @@ -57,59 +58,59 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default"); try (AdminClient adminClient = createAdminClient(requestParams)) { Set strings = adminClient.listTopics().names().get(); return new ArrayList<>(strings); } catch (Exception ex) { throw new DataSourcePluginException( - "check kafka connectivity failed, " + ex.getMessage(), ex); + "check kafka connectivity failed, " + ex.getMessage(), ex); } } @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { return DEFAULT_DATABASES; } @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try (AdminClient adminClient = createAdminClient(requestParams)) { // just test the connection DescribeClusterResult describeClusterResult = - adminClient.describeCluster(DEFAULT_TIMEOUT_OPTIONS); + adminClient.describeCluster(DEFAULT_TIMEOUT_OPTIONS); return CollectionUtils.isNotEmpty(describeClusterResult.nodes().get()); } catch (Exception ex) { throw new DataSourcePluginException( - "check kafka connectivity failed, " + ex.getMessage(), ex); + "check kafka connectivity failed, " + ex.getMessage(), ex); } } @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default"); return Collections.emptyList(); } @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default"); return Collections.emptyMap(); } private AdminClient createAdminClient(Map requestParams) { return AdminClient.create( - KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams)); + KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams)); } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java index bcba81c0e..16b258cdf 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java @@ -42,13 +42,13 @@ public String factoryIdentifier() { @Override public Set supportedDataSources() { return Sets.newHashSet( - DataSourcePluginInfo.builder() - .name(KAFKA_PLUGIN_NAME) - .icon(KAFKA_PLUGIN_ICON) - .version(KAFKA_PLUGIN_VERSION) - .supportVirtualTables(true) - .type(DatasourcePluginTypeEnum.NO_STRUCTURED.getCode()) - .build()); + DataSourcePluginInfo.builder() + .name(KAFKA_PLUGIN_NAME) + .icon(KAFKA_PLUGIN_ICON) + .version(KAFKA_PLUGIN_VERSION) + .supportVirtualTables(true) + .type(DatasourcePluginTypeEnum.NO_STRUCTURED.getCode()) + .build()); } @Override diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java index 95988e762..25e70c991 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java @@ -17,8 +17,6 @@ package org.apache.seatunnel.datasource.plugin.kafka; -import static com.google.common.base.Preconditions.checkArgument; - import org.apache.seatunnel.shade.com.typesafe.config.Config; import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; @@ -27,28 +25,30 @@ import java.util.Map; import java.util.Properties; +import static com.google.common.base.Preconditions.checkArgument; + public class KafkaRequestParamsUtils { public static Properties parsePropertiesFromRequestParams(Map requestParams) { checkArgument( - requestParams.containsKey(KafkaOptionRule.BOOTSTRAP_SERVERS.key()), - String.format( - "Missing %s in requestParams", KafkaOptionRule.BOOTSTRAP_SERVERS.key())); + requestParams.containsKey(KafkaOptionRule.BOOTSTRAP_SERVERS.key()), + String.format( + "Missing %s in requestParams", KafkaOptionRule.BOOTSTRAP_SERVERS.key())); final Properties properties = new Properties(); properties.put( - AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, - requestParams.get(KafkaOptionRule.BOOTSTRAP_SERVERS.key())); + AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, + requestParams.get(KafkaOptionRule.BOOTSTRAP_SERVERS.key())); if (requestParams.containsKey(KafkaOptionRule.KAFKA_CONFIG.key())) { Config configObject = - ConfigFactory.parseString( - requestParams.get(KafkaOptionRule.KAFKA_CONFIG.key())); + ConfigFactory.parseString( + requestParams.get(KafkaOptionRule.KAFKA_CONFIG.key())); configObject - .entrySet() - .forEach( - entry -> { - properties.put( - entry.getKey(), entry.getValue().unwrapped().toString()); - }); + .entrySet() + .forEach( + entry -> { + properties.put( + entry.getKey(), entry.getValue().unwrapped().toString()); + }); } return properties; } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java index e694de45a..cdcb4ffa9 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java @@ -20,12 +20,13 @@ import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.datasource.plugin.api.model.TableField; -import com.google.common.collect.ImmutableMap; -import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import com.google.common.collect.ImmutableMap; +import lombok.extern.slf4j.Slf4j; + import java.util.Collections; import java.util.List; import java.util.Map; @@ -36,37 +37,37 @@ public class KafkaDataSourceChannelTest { private static final KafkaDataSourceChannel KAFKA_DATA_SOURCE_CHANNEL = - new KafkaDataSourceChannel(); + new KafkaDataSourceChannel(); private static final String KAFKA_PLUGIN_NAME = "kafka"; private static final String BOOTSTRAP_SERVER = "localhost:9092"; private static final Map REQUEST_PARAMS = - new ImmutableMap.Builder() - .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), BOOTSTRAP_SERVER) - .build(); + new ImmutableMap.Builder() + .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), BOOTSTRAP_SERVER) + .build(); @Test public void getDataSourceOptions() { OptionRule dataSourceMetadataFieldsByDataSourceName = - KAFKA_DATA_SOURCE_CHANNEL.getDataSourceOptions(KAFKA_PLUGIN_NAME); + KAFKA_DATA_SOURCE_CHANNEL.getDataSourceOptions(KAFKA_PLUGIN_NAME); Assertions.assertEquals( - 1, dataSourceMetadataFieldsByDataSourceName.getRequiredOptions().size()); + 1, dataSourceMetadataFieldsByDataSourceName.getRequiredOptions().size()); } @Test public void getDatasourceMetadataFieldsByDataSourceName() { OptionRule datasourceMetadataFieldsByDataSourceName = - KAFKA_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName( - KAFKA_PLUGIN_NAME); + KAFKA_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName( + KAFKA_PLUGIN_NAME); Assertions.assertEquals( - 2, datasourceMetadataFieldsByDataSourceName.getOptionalOptions().size()); + 2, datasourceMetadataFieldsByDataSourceName.getOptionalOptions().size()); } @Test public void getTables() { List tables = - KAFKA_DATA_SOURCE_CHANNEL.getTables(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, null); + KAFKA_DATA_SOURCE_CHANNEL.getTables(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, null); log.info("{}", tables); Assertions.assertNotNull(tables); } @@ -74,7 +75,7 @@ public void getTables() { @Test public void getDatabases() { List databases = - KAFKA_DATA_SOURCE_CHANNEL.getDatabases(KAFKA_PLUGIN_NAME, REQUEST_PARAMS); + KAFKA_DATA_SOURCE_CHANNEL.getDatabases(KAFKA_PLUGIN_NAME, REQUEST_PARAMS); log.info("{}", databases); Assertions.assertNotNull(databases); } @@ -82,15 +83,15 @@ public void getDatabases() { @Test public void checkDataSourceConnectivity() { boolean dataSourceConnectivity = - KAFKA_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity( - KAFKA_PLUGIN_NAME, REQUEST_PARAMS); + KAFKA_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity( + KAFKA_PLUGIN_NAME, REQUEST_PARAMS); Assertions.assertTrue(dataSourceConnectivity); } @Test public void getTableFields() { List tableFields = - KAFKA_DATA_SOURCE_CHANNEL.getTableFields(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", ""); + KAFKA_DATA_SOURCE_CHANNEL.getTableFields(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", ""); log.info("{}", tableFields); Assertions.assertTrue(tableFields.isEmpty()); } @@ -98,8 +99,8 @@ public void getTableFields() { @Test public void testGetTableFields() { Map> tableFields = - KAFKA_DATA_SOURCE_CHANNEL.getTableFields( - KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", Collections.emptyList()); + KAFKA_DATA_SOURCE_CHANNEL.getTableFields( + KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", Collections.emptyList()); log.info("{}", tableFields); Assertions.assertTrue(tableFields.isEmpty()); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java index 67d8d33f8..2ce0842fa 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java @@ -17,10 +17,11 @@ package org.apache.seatunnel.datasource.plugin.kafka; -import com.google.common.collect.ImmutableMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import com.google.common.collect.ImmutableMap; + import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -30,37 +31,41 @@ class KafkaRequestParamsUtilsTest { @Test void parsePropertiesFromRequestParams() { Map requestParams = - new ImmutableMap.Builder() - .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092") - .put( - KafkaOptionRule.KAFKA_CONFIG.key(), - "{" + "security.protocol = SASL_PLAINTEXT" + "}") - .build(); + new ImmutableMap.Builder() + .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092") + .put( + KafkaOptionRule.KAFKA_CONFIG.key(), + "{" + "security.protocol = SASL_PLAINTEXT" + "}") + .build(); Properties properties = - KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams); + KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams); Assertions.assertEquals("SASL_PLAINTEXT", properties.getProperty("security.protocol")); } @Test void parsePropertiesFromRequestParamsBadCase() { Assertions.assertDoesNotThrow( - () -> - KafkaRequestParamsUtils.parsePropertiesFromRequestParams( - new ImmutableMap.Builder() - .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092") - .put(KafkaOptionRule.KAFKA_CONFIG.key(), "{}") - .build())); + () -> + KafkaRequestParamsUtils.parsePropertiesFromRequestParams( + new ImmutableMap.Builder() + .put( + KafkaOptionRule.BOOTSTRAP_SERVERS.key(), + "localhost:9092") + .put(KafkaOptionRule.KAFKA_CONFIG.key(), "{}") + .build())); Assertions.assertThrows( - IllegalArgumentException.class, - () -> KafkaRequestParamsUtils.parsePropertiesFromRequestParams(new HashMap<>())); + IllegalArgumentException.class, + () -> KafkaRequestParamsUtils.parsePropertiesFromRequestParams(new HashMap<>())); Assertions.assertDoesNotThrow( - () -> - KafkaRequestParamsUtils.parsePropertiesFromRequestParams( - new ImmutableMap.Builder() - .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092") - .put(KafkaOptionRule.KAFKA_CONFIG.key(), "") - .build())); + () -> + KafkaRequestParamsUtils.parsePropertiesFromRequestParams( + new ImmutableMap.Builder() + .put( + KafkaOptionRule.BOOTSTRAP_SERVERS.key(), + "localhost:9092") + .put(KafkaOptionRule.KAFKA_CONFIG.key(), "") + .build())); } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java index 07a9f3859..dd26ac14d 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java @@ -22,9 +22,10 @@ import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.commons.lang3.StringUtils; + import com.google.common.collect.Sets; import lombok.NonNull; -import org.apache.commons.lang3.StringUtils; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -42,7 +43,7 @@ public class MysqlCDCDataSourceChannel implements DataSourceChannel { public static final Set MYSQL_SYSTEM_DATABASES = - Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys"); + Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys"); @Override public boolean canAbleGetSchema() { @@ -61,7 +62,7 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - String pluginName, Map requestParams, String database) { + String pluginName, Map requestParams, String database) { return this.getTableNames(requestParams, database); } @@ -76,22 +77,22 @@ public List getDatabases(String pluginName, Map requestP @Override public boolean checkDataSourceConnectivity( - String pluginName, Map requestParams) { + String pluginName, Map requestParams) { return this.checkJdbcConnectivity(requestParams); } @Override public List getTableFields( - String pluginName, Map requestParams, String database, String table) { + String pluginName, Map requestParams, String database, String table) { return getTableFields(requestParams, database, table); } @Override public Map> getTableFields( - String pluginName, - Map requestParams, - String database, - List tables) { + String pluginName, + Map requestParams, + String database, + List tables) { Map> tableFields = new HashMap<>(tables.size()); for (String table : tables) { tableFields.put(table, getTableFields(requestParams, database, table)); @@ -102,9 +103,9 @@ public Map> getTableFields( @SuppressWarnings("checkstyle:MagicNumber") protected boolean checkJdbcConnectivity(Map requestParams) { try (Connection connection = init(requestParams); - Statement statement = connection.createStatement()) { + Statement statement = connection.createStatement()) { - try (ResultSet resultSet = statement.executeQuery("SHOW MASTER STATUS");) { + try (ResultSet resultSet = statement.executeQuery("SHOW MASTER STATUS"); ) { if (resultSet.next()) { String binlogFile = resultSet.getString("File"); if (StringUtils.isBlank(binlogFile)) { @@ -116,7 +117,7 @@ protected boolean checkJdbcConnectivity(Map requestParams) { } try (ResultSet resultSet = - statement.executeQuery("SHOW VARIABLES LIKE 'binlog_format'")) { + statement.executeQuery("SHOW VARIABLES LIKE 'binlog_format'")) { if (resultSet.next()) { String binlogFormat = resultSet.getString("Value"); if (!"ROW".equalsIgnoreCase(binlogFormat)) { @@ -128,7 +129,7 @@ protected boolean checkJdbcConnectivity(Map requestParams) { } try (ResultSet resultSet = - statement.executeQuery("SHOW VARIABLES LIKE 'binlog_row_image'")) { + statement.executeQuery("SHOW VARIABLES LIKE 'binlog_row_image'")) { if (resultSet.next()) { String binlogRowImage = resultSet.getString("Value"); if (!"FULL".equalsIgnoreCase(binlogRowImage)) { @@ -141,7 +142,7 @@ protected boolean checkJdbcConnectivity(Map requestParams) { return true; } catch (Exception e) { throw new DataSourcePluginException( - "check jdbc connectivity failed, " + e.getMessage(), e); + "check jdbc connectivity failed, " + e.getMessage(), e); } } @@ -151,7 +152,7 @@ protected Connection init(Map requestParams) throws SQLException } String url = requestParams.get(MysqlCDCOptionRule.BASE_URL.key()); if (null != requestParams.get(MysqlCDCOptionRule.PASSWORD.key()) - && null != requestParams.get(MysqlCDCOptionRule.USERNAME.key())) { + && null != requestParams.get(MysqlCDCOptionRule.USERNAME.key())) { String username = requestParams.get(MysqlCDCOptionRule.USERNAME.key()); String password = requestParams.get(MysqlCDCOptionRule.PASSWORD.key()); return DriverManager.getConnection(url, username, password); @@ -162,8 +163,8 @@ protected Connection init(Map requestParams) throws SQLException protected List getDataBaseNames(Map requestParams) throws SQLException { List dbNames = new ArrayList<>(); try (Connection connection = init(requestParams); - PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;"); + ResultSet re = statement.executeQuery()) { // filter system databases while (re.next()) { String dbName = re.getString("database"); @@ -178,10 +179,10 @@ protected List getDataBaseNames(Map requestParams) throw protected List getTableNames(Map requestParams, String dbName) { List tableNames = new ArrayList<>(); try (Connection connection = init(requestParams); - ResultSet resultSet = - connection - .getMetaData() - .getTables(dbName, null, null, new String[]{"TABLE"})) { + ResultSet resultSet = + connection + .getMetaData() + .getTables(dbName, null, null, new String[] {"TABLE"})) { while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -195,9 +196,9 @@ protected List getTableNames(Map requestParams, String d } protected List getTableFields( - Map requestParams, String dbName, String tableName) { + Map requestParams, String dbName, String tableName) { List tableFields = new ArrayList<>(); - try (Connection connection = init(requestParams);) { + try (Connection connection = init(requestParams); ) { DatabaseMetaData metaData = connection.getMetaData(); String primaryKey = getPrimaryKey(metaData, dbName, tableName); ResultSet resultSet = metaData.getColumns(dbName, null, tableName, null); @@ -223,7 +224,7 @@ protected List getTableFields( } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -233,8 +234,8 @@ private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String ta private boolean isNotSystemDatabase(String dbName) { return MYSQL_SYSTEM_DATABASES.stream() - .noneMatch( - systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName)); + .noneMatch( + systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName)); } private boolean convertToBoolean(Object value) { diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java index d496945de..3c47d5a51 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java @@ -17,11 +17,11 @@ package org.apache.seatunnel.datasource.plugin.api; -import static com.google.common.base.Preconditions.checkNotNull; - import lombok.Builder; import lombok.Data; +import static com.google.common.base.Preconditions.checkNotNull; + @Data @Builder public class DataSourcePluginInfo { @@ -32,18 +32,14 @@ public class DataSourcePluginInfo { public String version; - /** - * @see DatasourcePluginTypeEnum - */ + /** @see DatasourcePluginTypeEnum */ private Integer type; - /** - * whether support virtual tables, default false - */ + /** whether support virtual tables, default false */ private Boolean supportVirtualTables; public DataSourcePluginInfo( - String name, String icon, String version, Integer type, Boolean supportVirtualTables) { + String name, String icon, String version, Integer type, Boolean supportVirtualTables) { this.name = checkNotNull(name, "name can not be null"); this.icon = checkNotNull(icon, "icon can not be null"); this.version = checkNotNull(version, "version can not be null"); diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java index 2a27a5684..257d9f3d8 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java @@ -23,10 +23,10 @@ public class ParamtersUtils { /** - * for some parameters, we need to convert them to {@link Map} eg: s3Options { - * "access.value": "org.apache.hadoop.fs.s3a.S3AFileSystem", "access.key": - * "AKIAIOSFODNN7EXAMPLE", "hadoop_s3_properties": " fs.s3a.impl = - * org.apache.hadoop.fs.s3a.S3AFileSystem fs.s3a.access.key = AKIAIOSFODNN7EXAMPLE " + * for some parameters, we need to convert them to {@link Map} eg: s3Options { "access.value": + * "org.apache.hadoop.fs.s3a.S3AFileSystem", "access.key": "AKIAIOSFODNN7EXAMPLE", + * "hadoop_s3_properties": " fs.s3a.impl = org.apache.hadoop.fs.s3a.S3AFileSystem + * fs.s3a.access.key = AKIAIOSFODNN7EXAMPLE " * *

    Convert parameters to {@link Map} * diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java index f32561867..8da509c12 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java @@ -17,15 +17,16 @@ package org.apache.seatunnel.datasource.plugin.redshift.s3; -import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY; - -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; +import lombok.extern.slf4j.Slf4j; + import java.util.Arrays; import java.util.Map; +import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY; + @Slf4j public class HadoopS3AConfiguration { @@ -42,11 +43,11 @@ public static Configuration getConfiguration(Map s3Options) { if (!s3Options.containsKey(S3RedshiftOptionRule.BUCKET.key())) { throw new IllegalArgumentException( - "S3Redshift datasource bucket is null, please check your config"); + "S3Redshift datasource bucket is null, please check your config"); } if (!s3Options.containsKey(S3RedshiftOptionRule.FS_S3A_ENDPOINT.key())) { throw new IllegalArgumentException( - "S3Redshift datasource endpoint is null, please check your config"); + "S3Redshift datasource endpoint is null, please check your config"); } String bucket = s3Options.get(S3RedshiftOptionRule.BUCKET.key()); @@ -58,38 +59,38 @@ public static Configuration getConfiguration(Map s3Options) { Configuration hadoopConf = new Configuration(); hadoopConf.set(FS_DEFAULT_NAME_KEY, bucket); hadoopConf.set( - S3RedshiftOptionRule.FS_S3A_ENDPOINT.key(), - s3Options.get(S3RedshiftOptionRule.FS_S3A_ENDPOINT.key())); + S3RedshiftOptionRule.FS_S3A_ENDPOINT.key(), + s3Options.get(S3RedshiftOptionRule.FS_S3A_ENDPOINT.key())); hadoopConf.set(formatKey(protocol, HDFS_IMPL_KEY), fsImpl); if (s3Options.containsKey(S3RedshiftOptionRule.HADOOP_S3_PROPERTIES.key())) { Arrays.stream( - s3Options - .get(S3RedshiftOptionRule.HADOOP_S3_PROPERTIES.key()) - .split("\n")) - .map(String::trim) - .filter(StringUtils::isNotBlank) - .forEach( - line -> { - String[] kv = line.split("="); - if (kv.length == 2) { - hadoopConf.set(kv[0].trim(), kv[1].trim()); - } - }); + s3Options + .get(S3RedshiftOptionRule.HADOOP_S3_PROPERTIES.key()) + .split("\n")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .forEach( + line -> { + String[] kv = line.split("="); + if (kv.length == 2) { + hadoopConf.set(kv[0].trim(), kv[1].trim()); + } + }); } if (S3RedshiftOptionRule.S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider - .getProvider() - .equals(s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) { + .getProvider() + .equals(s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) { hadoopConf.set( - S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), - s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); hadoopConf.set( - "fs.s3a.access.key", s3Options.get(S3RedshiftOptionRule.ACCESS_KEY.key())); + "fs.s3a.access.key", s3Options.get(S3RedshiftOptionRule.ACCESS_KEY.key())); hadoopConf.set( - "fs.s3a.secret.key", s3Options.get(S3RedshiftOptionRule.SECRET_KEY.key())); + "fs.s3a.secret.key", s3Options.get(S3RedshiftOptionRule.SECRET_KEY.key())); } else { hadoopConf.set( - S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), - s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); } return hadoopConf; } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java index 07aceb327..de9e2b09e 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java @@ -23,13 +23,14 @@ import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils; -import com.google.common.collect.Sets; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; +import com.google.common.collect.Sets; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; @@ -59,13 +60,13 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { return getTableNames(requestParams, database); } @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try { return getDataBaseNames(pluginName, requestParams); } catch (SQLException e) { @@ -75,7 +76,7 @@ public List getDatabases( @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { checkHdfsS3Connection(requestParams); checkJdbcConnection(requestParams); return true; @@ -83,19 +84,19 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { return getTableFields(requestParams, database, table); } @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { // not need this method return null; } @@ -113,14 +114,14 @@ private void checkJdbcConnection(Map requestParams) { return; } catch (SQLException e) { throw new DataSourcePluginException( - "Check Redshift jdbc connection failed,please check your config", e); + "Check Redshift jdbc connection failed,please check your config", e); } } try (Connection ignored = DriverManager.getConnection(jdbcUrl, username, password)) { log.info("Redshift jdbc connection is valid"); } catch (SQLException e) { throw new DataSourcePluginException( - "Check Redshift jdbc connection failed,please check your config", e); + "Check Redshift jdbc connection failed,please check your config", e); } } @@ -130,20 +131,20 @@ private void checkHdfsS3Connection(Map requestParams) { fs.getFileStatus(new org.apache.hadoop.fs.Path("/")); } catch (IOException e) { throw new DataSourcePluginException( - "S3 configuration is invalid, please check your config", e); + "S3 configuration is invalid, please check your config", e); } } protected Connection init(Map requestParams, String databaseName) - throws SQLException { + throws SQLException { if (null == requestParams.get(S3RedshiftOptionRule.JDBC_URL.key())) { throw new DataSourcePluginException("Jdbc url is null"); } String url = - JdbcUtils.replaceDatabase( - requestParams.get(S3RedshiftOptionRule.JDBC_URL.key()), databaseName); + JdbcUtils.replaceDatabase( + requestParams.get(S3RedshiftOptionRule.JDBC_URL.key()), databaseName); if (null != requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key()) - && null != requestParams.get(S3RedshiftOptionRule.JDBC_USER.key())) { + && null != requestParams.get(S3RedshiftOptionRule.JDBC_USER.key())) { String username = requestParams.get(S3RedshiftOptionRule.JDBC_USER.key()); String password = requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key()); return DriverManager.getConnection(url, username, password); @@ -152,12 +153,12 @@ protected Connection init(Map requestParams, String databaseName } protected List getDataBaseNames(String pluginName, Map requestParams) - throws SQLException { + throws SQLException { List dbNames = new ArrayList<>(); try (Connection connection = init(requestParams, null); - PreparedStatement statement = - connection.prepareStatement("select datname from pg_database;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = + connection.prepareStatement("select datname from pg_database;"); + ResultSet re = statement.executeQuery()) { while (re.next()) { String dbName = re.getString("datname"); if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(dbName)) { @@ -172,9 +173,9 @@ protected List getDataBaseNames(String pluginName, Map r protected List getTableNames(Map requestParams, String dbName) { List tableNames = new ArrayList<>(); - try (Connection connection = init(requestParams, dbName);) { + try (Connection connection = init(requestParams, dbName); ) { ResultSet resultSet = - connection.getMetaData().getTables(dbName, null, null, new String[]{"TABLE"}); + connection.getMetaData().getTables(dbName, null, null, new String[] {"TABLE"}); while (resultSet.next()) { String tableName = resultSet.getString("TABLE_NAME"); if (StringUtils.isNotBlank(tableName)) { @@ -188,15 +189,15 @@ protected List getTableNames(Map requestParams, String d } protected List getTableFields( - Map requestParams, String dbName, String tableName) { + Map requestParams, String dbName, String tableName) { List tableFields = new ArrayList<>(); - try (Connection connection = init(requestParams, dbName);) { + try (Connection connection = init(requestParams, dbName); ) { DatabaseMetaData metaData = connection.getMetaData(); String primaryKey = getPrimaryKey(metaData, dbName, tableName); String[] split = tableName.split("\\."); if (split.length != 2) { throw new DataSourcePluginException( - "Postgresql tableName should composed by schemaName.tableName"); + "Postgresql tableName should composed by schemaName.tableName"); } ResultSet resultSet = metaData.getColumns(dbName, split[0], split[1], null); while (resultSet.next()) { @@ -221,7 +222,7 @@ protected List getTableFields( } private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName) - throws SQLException { + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -255,14 +256,14 @@ private boolean convertToBoolean(Object value) { } public static final Set POSTGRESQL_SYSTEM_DATABASES = - Sets.newHashSet( - "information_schema", - "pg_catalog", - "root", - "pg_toast", - "pg_temp_1", - "pg_toast_temp_1", - "postgres", - "template0", - "template1"); + Sets.newHashSet( + "information_schema", + "pg_catalog", + "root", + "pg_toast", + "pg_temp_1", + "pg_toast_temp_1", + "postgres", + "template0", + "template1"); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java index 3ef212acd..ee6aa4ba0 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java @@ -37,14 +37,14 @@ public String factoryIdentifier() { @Override public Set supportedDataSources() { DataSourcePluginInfo s3DatasourcePluginInfo = - DataSourcePluginInfo.builder() - .name("S3-Redshift") - .type(DatasourcePluginTypeEnum.DATABASE.getCode()) - .version("1.0.0") - .supportVirtualTables(false) - .icon("S3-Redshift") - .icon("S3-Redshift") - .build(); + DataSourcePluginInfo.builder() + .name("S3-Redshift") + .type(DatasourcePluginTypeEnum.DATABASE.getCode()) + .version("1.0.0") + .supportVirtualTables(false) + .icon("S3-Redshift") + .icon("S3-Redshift") + .build(); return Sets.newHashSet(s3DatasourcePluginInfo); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java index ecce9793c..3c18b2965 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java @@ -17,16 +17,17 @@ package org.apache.seatunnel.datasource.plugin.s3; -import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY; - import org.apache.seatunnel.shade.com.typesafe.config.Config; import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory; -import lombok.extern.slf4j.Slf4j; import org.apache.hadoop.conf.Configuration; +import lombok.extern.slf4j.Slf4j; + import java.util.Map; +import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY; + @Slf4j public class HadoopS3AConfiguration { @@ -43,11 +44,11 @@ public static Configuration getConfiguration(Map s3Options) { if (!s3Options.containsKey(S3OptionRule.BUCKET.key())) { throw new IllegalArgumentException( - "S3 datasource bucket is null, please check your config"); + "S3 datasource bucket is null, please check your config"); } if (!s3Options.containsKey(S3OptionRule.FS_S3A_ENDPOINT.key())) { throw new IllegalArgumentException( - "S3 datasource endpoint is null, please check your config"); + "S3 datasource endpoint is null, please check your config"); } String bucket = s3Options.get(S3OptionRule.BUCKET.key()); @@ -59,32 +60,32 @@ public static Configuration getConfiguration(Map s3Options) { Configuration hadoopConf = new Configuration(); hadoopConf.set(FS_DEFAULT_NAME_KEY, bucket); hadoopConf.set( - S3OptionRule.FS_S3A_ENDPOINT.key(), - s3Options.get(S3OptionRule.FS_S3A_ENDPOINT.key())); + S3OptionRule.FS_S3A_ENDPOINT.key(), + s3Options.get(S3OptionRule.FS_S3A_ENDPOINT.key())); hadoopConf.set(formatKey(protocol, HDFS_IMPL_KEY), fsImpl); if (s3Options.containsKey(S3OptionRule.HADOOP_S3_PROPERTIES.key())) { Config configObject = - ConfigFactory.parseString( - s3Options.get(S3OptionRule.HADOOP_S3_PROPERTIES.key())); + ConfigFactory.parseString( + s3Options.get(S3OptionRule.HADOOP_S3_PROPERTIES.key())); configObject - .entrySet() - .forEach( - entry -> { - hadoopConf.set( - entry.getKey(), entry.getValue().unwrapped().toString()); - }); + .entrySet() + .forEach( + entry -> { + hadoopConf.set( + entry.getKey(), entry.getValue().unwrapped().toString()); + }); } if (S3OptionRule.S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider.getProvider() - .equals(s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) { + .equals(s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) { hadoopConf.set( - S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), - s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); hadoopConf.set("fs.s3a.access.key", s3Options.get(S3OptionRule.ACCESS_KEY.key())); hadoopConf.set("fs.s3a.secret.key", s3Options.get(S3OptionRule.SECRET_KEY.key())); } else { hadoopConf.set( - S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), - s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); + S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(), + s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key())); } return hadoopConf; } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java index 640b6f16f..5b2236562 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java @@ -40,13 +40,13 @@ public String factoryIdentifier() { @Override public Set supportedDataSources() { DataSourcePluginInfo s3DatasourcePluginInfo = - DataSourcePluginInfo.builder() - .name(PLUGIN_NAME) - .type(DatasourcePluginTypeEnum.FILE.getCode()) - .version("1.0.0") - .supportVirtualTables(false) - .icon("S3File") - .build(); + DataSourcePluginInfo.builder() + .name(PLUGIN_NAME) + .type(DatasourcePluginTypeEnum.FILE.getCode()) + .version("1.0.0") + .supportVirtualTables(false) + .icon("S3File") + .build(); return Sets.newHashSet(s3DatasourcePluginInfo); } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java index e1ba8de5e..cf0ee45b9 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java @@ -22,11 +22,12 @@ import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; -import lombok.NonNull; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; +import lombok.NonNull; + import java.io.IOException; import java.util.List; import java.util.Map; @@ -44,46 +45,46 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { throw new UnsupportedOperationException("getTables is not supported for S3 datasource"); } @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { throw new UnsupportedOperationException("getDatabases is not supported for S3 datasource"); } @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { Configuration conf = HadoopS3AConfiguration.getConfiguration(requestParams); try (FileSystem fs = FileSystem.get(conf)) { fs.listStatus(new Path("/")); return true; } catch (IOException e) { throw new DataSourcePluginException( - String.format("check s3 connectivity failed, config is: %s", requestParams), e); + String.format("check s3 connectivity failed, config is: %s", requestParams), e); } } @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { throw new UnsupportedOperationException( - "getTableFields is not supported for S3 datasource"); + "getTableFields is not supported for S3 datasource"); } @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { throw new UnsupportedOperationException( - "getTableFields is not supported for S3 datasource"); + "getTableFields is not supported for S3 datasource"); } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java index 4fed9948f..20a23fae0 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java @@ -22,11 +22,12 @@ import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; + import com.google.common.collect.Sets; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -45,7 +46,7 @@ public class SqlServerCDCDataSourceChannel implements DataSourceChannel { public static final Set MYSQL_SYSTEM_DATABASES = - Sets.newHashSet("master", "tempdb", "model", "msdb"); + Sets.newHashSet("master", "tempdb", "model", "msdb"); @Override public boolean canAbleGetSchema() { @@ -64,7 +65,7 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - String pluginName, Map requestParams, String database) { + String pluginName, Map requestParams, String database) { return this.getTableNames(requestParams, database); } @@ -79,10 +80,10 @@ public List getDatabases(String pluginName, Map requestP @Override public boolean checkDataSourceConnectivity( - String pluginName, Map requestParams) { + String pluginName, Map requestParams) { try (Connection connection = init(requestParams); - PreparedStatement statement = connection.prepareStatement("SELECT 1"); - ResultSet rs = statement.executeQuery()) { + PreparedStatement statement = connection.prepareStatement("SELECT 1"); + ResultSet rs = statement.executeQuery()) { return rs.next(); } catch (SQLException e) { throw new DataSourcePluginException("connect datasource failed", e); @@ -91,17 +92,17 @@ public boolean checkDataSourceConnectivity( @Override public List getTableFields( - String pluginName, Map requestParams, String database, String table) { + String pluginName, Map requestParams, String database, String table) { Pair pair = parseSchemaAndTable(table); return getTableFields(requestParams, database, pair.getLeft(), pair.getRight()); } @Override public Map> getTableFields( - String pluginName, - Map requestParams, - String database, - List tables) { + String pluginName, + Map requestParams, + String database, + List tables) { Map> tableFields = new HashMap<>(tables.size()); for (String table : tables) { tableFields.put(table, getTableFields(pluginName, requestParams, database, table)); @@ -115,7 +116,7 @@ private Connection init(Map requestParams) throws SQLException { } String url = requestParams.get(SqlServerCDCOptionRule.BASE_URL.key()); if (null != requestParams.get(SqlServerCDCOptionRule.PASSWORD.key()) - && null != requestParams.get(SqlServerCDCOptionRule.USERNAME.key())) { + && null != requestParams.get(SqlServerCDCOptionRule.USERNAME.key())) { String username = requestParams.get(SqlServerCDCOptionRule.USERNAME.key()); String password = requestParams.get(SqlServerCDCOptionRule.PASSWORD.key()); return DriverManager.getConnection(url, username, password); @@ -126,10 +127,10 @@ private Connection init(Map requestParams) throws SQLException { private List getDataBaseNames(Map requestParams) throws SQLException { List dbNames = new ArrayList<>(); try (Connection connection = init(requestParams); - PreparedStatement statement = - connection.prepareStatement( - "SELECT NAME FROM SYS.DATABASES WHERE IS_CDC_ENABLED = 1;"); - ResultSet re = statement.executeQuery()) { + PreparedStatement statement = + connection.prepareStatement( + "SELECT NAME FROM SYS.DATABASES WHERE IS_CDC_ENABLED = 1;"); + ResultSet re = statement.executeQuery()) { // filter system databases while (re.next()) { String dbName = re.getString("NAME"); @@ -144,18 +145,18 @@ private List getDataBaseNames(Map requestParams) throws private List getTableNames(Map requestParams, String dbName) { final String sql = - String.format( - "SELECT SCHEMAS.NAME AS SCHEMA_NAME, TABLES.NAME AS TABLE_NAME" - + " FROM %s.SYS.SCHEMAS AS SCHEMAS" - + " JOIN %s.SYS.TABLES AS TABLES" - + " ON SCHEMAS.SCHEMA_ID = TABLES.SCHEMA_ID" - + " AND TABLES.IS_TRACKED_BY_CDC = 1", - dbName, dbName); + String.format( + "SELECT SCHEMAS.NAME AS SCHEMA_NAME, TABLES.NAME AS TABLE_NAME" + + " FROM %s.SYS.SCHEMAS AS SCHEMAS" + + " JOIN %s.SYS.TABLES AS TABLES" + + " ON SCHEMAS.SCHEMA_ID = TABLES.SCHEMA_ID" + + " AND TABLES.IS_TRACKED_BY_CDC = 1", + dbName, dbName); List tableNames = new ArrayList<>(); try (Connection connection = init(requestParams); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery(sql)) { + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { while (resultSet.next()) { String schemaName = resultSet.getString("SCHEMA_NAME"); String tableName = resultSet.getString("TABLE_NAME"); @@ -168,9 +169,9 @@ private List getTableNames(Map requestParams, String dbN } private List getTableFields( - Map requestParams, String dbName, String schemaName, String tableName) { + Map requestParams, String dbName, String schemaName, String tableName) { List tableFields = new ArrayList<>(); - try (Connection connection = init(requestParams);) { + try (Connection connection = init(requestParams); ) { DatabaseMetaData metaData = connection.getMetaData(); String primaryKey = getPrimaryKey(metaData, dbName, schemaName, tableName); ResultSet resultSet = metaData.getColumns(dbName, schemaName, tableName, null); @@ -196,8 +197,8 @@ private List getTableFields( } private String getPrimaryKey( - DatabaseMetaData metaData, String dbName, String schemaName, String tableName) - throws SQLException { + DatabaseMetaData metaData, String dbName, String schemaName, String tableName) + throws SQLException { ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, schemaName, tableName); while (primaryKeysInfo.next()) { return primaryKeysInfo.getString("COLUMN_NAME"); @@ -207,8 +208,8 @@ private String getPrimaryKey( private boolean isNotSystemDatabase(String dbName) { return MYSQL_SYSTEM_DATABASES.stream() - .noneMatch( - systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName)); + .noneMatch( + systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName)); } private boolean convertToBoolean(Object value) { diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java index 321f9c8c6..bc4f276f1 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java @@ -37,7 +37,7 @@ public String factoryIdentifier() { @Override public Set supportedDataSources() { return Collections.singleton( - SqlServerCDCDataSourceConfig.SQLSERVER_CDC_DATASOURCE_PLUGIN_INFO); + SqlServerCDCDataSourceConfig.SQLSERVER_CDC_DATASOURCE_PLUGIN_INFO); } @Override diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java index b815a3187..982c50721 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java @@ -39,10 +39,17 @@ public void testConnect() { requestParams.put("username", "sa"); requestParams.put("password", "MyPass@word"); - for (String database : channel.getDatabases(SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams)) { - final List tables = channel.getTables(SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams, database); + for (String database : + channel.getDatabases(SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams)) { + final List tables = + channel.getTables( + SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams, database); final Map> tableFields = - channel.getTableFields(SqlServerCDCDataSourceConfig.PLUGIN_NAME, requestParams, database, tables); + channel.getTableFields( + SqlServerCDCDataSourceConfig.PLUGIN_NAME, + requestParams, + database, + tables); } } } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java index c04558b07..4acc43e60 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksCatalog.java @@ -17,8 +17,6 @@ package org.apache.seatunnel.datasource.plugin.starrocks; -import static com.google.common.base.Preconditions.checkArgument; - import org.apache.seatunnel.api.table.catalog.PrimaryKey; import org.apache.seatunnel.api.table.catalog.TablePath; import org.apache.seatunnel.api.table.catalog.exception.CatalogException; @@ -27,6 +25,7 @@ import org.apache.seatunnel.datasource.plugin.api.model.TableField; import org.apache.commons.lang3.StringUtils; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,6 +41,8 @@ import java.util.Optional; import java.util.Set; +import static com.google.common.base.Preconditions.checkArgument; + public class StarRocksCatalog { protected final String catalogName; @@ -79,8 +80,8 @@ public StarRocksCatalog(String catalogName, String username, String pwd, String public List listDatabases() throws CatalogException { List databases = new ArrayList<>(); try (Connection conn = DriverManager.getConnection(defaultUrl, username, pwd); - PreparedStatement ps = conn.prepareStatement("SHOW DATABASES;"); - ResultSet rs = ps.executeQuery();) { + PreparedStatement ps = conn.prepareStatement("SHOW DATABASES;"); + ResultSet rs = ps.executeQuery(); ) { while (rs.next()) { String databaseName = rs.getString(1); @@ -92,19 +93,19 @@ public List listDatabases() throws CatalogException { return databases; } catch (Exception e) { throw new CatalogException( - String.format("Failed listing database in catalog %s", this.catalogName), e); + String.format("Failed listing database in catalog %s", this.catalogName), e); } } public List listTables(String databaseName) - throws CatalogException, DatabaseNotExistException { + throws CatalogException, DatabaseNotExistException { if (!databaseExists(databaseName)) { throw new DatabaseNotExistException(this.catalogName, databaseName); } try (Connection conn = DriverManager.getConnection(baseUrl + databaseName, username, pwd); - PreparedStatement ps = conn.prepareStatement("SHOW TABLES;"); - ResultSet rs = ps.executeQuery()) { + PreparedStatement ps = conn.prepareStatement("SHOW TABLES;"); + ResultSet rs = ps.executeQuery()) { List tables = new ArrayList<>(); @@ -115,29 +116,29 @@ public List listTables(String databaseName) return tables; } catch (Exception e) { throw new CatalogException( - String.format("Failed listing database in catalog %s", catalogName), e); + String.format("Failed listing database in catalog %s", catalogName), e); } } public List getTable(TablePath tablePath) - throws CatalogException, TableNotExistException { + throws CatalogException, TableNotExistException { if (!tableExists(tablePath)) { throw new TableNotExistException(catalogName, tablePath); } String dbUrl = baseUrl + tablePath.getDatabaseName(); try (Connection conn = DriverManager.getConnection(dbUrl, username, pwd); - PreparedStatement statement = - conn.prepareStatement( - String.format( - "SELECT * FROM %s WHERE 1 = 0;", - String.format( - "`%s`.`%s`", - tablePath.getDatabaseName(), - tablePath.getTableName())));) { + PreparedStatement statement = + conn.prepareStatement( + String.format( + "SELECT * FROM %s WHERE 1 = 0;", + String.format( + "`%s`.`%s`", + tablePath.getDatabaseName(), + tablePath.getTableName()))); ) { Optional primaryKey = - getPrimaryKey(tablePath.getDatabaseName(), tablePath.getTableName()); + getPrimaryKey(tablePath.getDatabaseName(), tablePath.getTableName()); ResultSetMetaData tableMetaData = statement.getMetaData(); @@ -148,13 +149,13 @@ public List getTable(TablePath tablePath) tableField.setType(tableMetaData.getColumnTypeName(i)); tableField.setComment(tableMetaData.getColumnLabel(i)); tableField.setNullable( - tableMetaData.isNullable(i) == ResultSetMetaData.columnNullable); + tableMetaData.isNullable(i) == ResultSetMetaData.columnNullable); tableField.setPrimaryKey( - primaryKey.isPresent() - && primaryKey - .get() - .getColumnNames() - .contains(tableField.getName())); + primaryKey.isPresent() + && primaryKey + .get() + .getColumnNames() + .contains(tableField.getName())); // TODO add default value tableField.setDefaultValue(null); fields.add(tableField); @@ -162,7 +163,7 @@ public List getTable(TablePath tablePath) return fields; } catch (Exception e) { throw new CatalogException( - String.format("Failed getting table %s", tablePath.getFullName()), e); + String.format("Failed getting table %s", tablePath.getFullName()), e); } } @@ -178,12 +179,12 @@ protected Optional getPrimaryKey(String schema, String table) throws List pkFields = new ArrayList<>(); try (Connection conn = DriverManager.getConnection(defaultUrl, username, pwd); - PreparedStatement statement = - conn.prepareStatement( - String.format( - "SELECT COLUMN_NAME FROM information_schema.columns where TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s' AND COLUMN_KEY = 'PRI' ORDER BY ORDINAL_POSITION", - schema, table)); - ResultSet rs = statement.executeQuery()) { + PreparedStatement statement = + conn.prepareStatement( + String.format( + "SELECT COLUMN_NAME FROM information_schema.columns where TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s' AND COLUMN_KEY = 'PRI' ORDER BY ORDINAL_POSITION", + schema, table)); + ResultSet rs = statement.executeQuery()) { while (rs.next()) { String columnName = rs.getString("COLUMN_NAME"); pkFields.add(columnName); @@ -216,7 +217,7 @@ public static boolean validateJdbcUrlWithDatabase(String url) { public boolean tableExists(TablePath tablePath) throws CatalogException { try { return databaseExists(tablePath.getDatabaseName()) - && listTables(tablePath.getDatabaseName()).contains(tablePath.getTableName()); + && listTables(tablePath.getDatabaseName()).contains(tablePath.getTableName()); } catch (DatabaseNotExistException e) { return false; } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java index a5ed265f4..835864a4f 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java +++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/StarRocksDataSourceChannel.java @@ -17,18 +17,19 @@ package org.apache.seatunnel.datasource.plugin.starrocks; +import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.ObjectMapper; + import org.apache.seatunnel.api.configuration.util.OptionRule; import org.apache.seatunnel.api.table.catalog.TablePath; import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel; import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException; import org.apache.seatunnel.datasource.plugin.api.model.TableField; -import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.ObjectMapper; - -import lombok.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import lombok.NonNull; + import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; @@ -59,21 +60,21 @@ public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pl @Override public List getTables( - @NonNull String pluginName, Map requestParams, String database) { + @NonNull String pluginName, Map requestParams, String database) { StarRocksCatalog catalog = getCatalog(requestParams); return catalog.listTables(database); } @Override public List getDatabases( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { StarRocksCatalog catalog = getCatalog(requestParams); return catalog.listDatabases(); } @Override public boolean checkDataSourceConnectivity( - @NonNull String pluginName, @NonNull Map requestParams) { + @NonNull String pluginName, @NonNull Map requestParams) { try { StarRocksCatalog catalog = getCatalog(requestParams); String nodeUrls = requestParams.get(StarRocksOptionRule.NODE_URLS.key()); @@ -85,7 +86,7 @@ public boolean checkDataSourceConnectivity( return true; } catch (Exception e) { throw new DataSourcePluginException( - "check StarRocks connectivity failed, " + e.getMessage(), e); + "check StarRocks connectivity failed, " + e.getMessage(), e); } } @@ -96,7 +97,7 @@ private static boolean telnet(String nodeUrl) throws IOException { try { String[] hostAndPort = nodeUrl.split(":"); socket.connect( - new InetSocketAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1])), 1000); + new InetSocketAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1])), 1000); isConnected = socket.isConnected(); } catch (IOException e) { LOGGER.error("telnet error", e); @@ -113,24 +114,24 @@ private static boolean telnet(String nodeUrl) throws IOException { @Override public List getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull String table) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull String table) { StarRocksCatalog catalog = getCatalog(requestParams); return catalog.getTable(TablePath.of(database, table)); } @Override public Map> getTableFields( - @NonNull String pluginName, - @NonNull Map requestParams, - @NonNull String database, - @NonNull List tables) { + @NonNull String pluginName, + @NonNull Map requestParams, + @NonNull String database, + @NonNull List tables) { StarRocksCatalog catalog = getCatalog(requestParams); Map> tableFields = new HashMap<>(); tables.forEach( - table -> tableFields.put(table, catalog.getTable(TablePath.of(database, table)))); + table -> tableFields.put(table, catalog.getTable(TablePath.of(database, table)))); return tableFields; } diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml index 91cdc7409..2336b7b0a 100644 --- a/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml +++ b/seatunnel-datasource/seatunnel-datasource-plugins/pom.xml @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. --> - 4.0.0 diff --git a/seatunnel-server/pom.xml b/seatunnel-server/pom.xml index 7bfbf9d67..0a623719c 100644 --- a/seatunnel-server/pom.xml +++ b/seatunnel-server/pom.xml @@ -13,15 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. --> - + 4.0.0 - seatunnel-web org.apache.seatunnel + seatunnel-web ${revision} - 4.0.0 seatunnel-server pom @@ -32,4 +31,4 @@ seatunnel-scheduler seatunnel-server-common - \ No newline at end of file + diff --git a/seatunnel-server/seatunnel-app/pom.xml b/seatunnel-server/seatunnel-app/pom.xml index ca908b06a..6c5da47b3 100644 --- a/seatunnel-server/seatunnel-app/pom.xml +++ b/seatunnel-server/seatunnel-app/pom.xml @@ -13,15 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. --> - + 4.0.0 - seatunnel-server org.apache.seatunnel + seatunnel-server ${revision} - 4.0.0 seatunnel-app @@ -83,8 +82,8 @@ spring-boot-starter-tomcat - log4j-to-slf4j org.apache.logging.log4j + log4j-to-slf4j @@ -125,16 +124,16 @@ ${springfox-swagger.version} - spring-aop org.springframework + spring-aop - spring-beans org.springframework + spring-beans - spring-context org.springframework + spring-context com.fasterxml.jackson.core @@ -158,8 +157,8 @@ hibernate-validator - classmate com.fasterxml + classmate @@ -188,8 +187,8 @@ ${project.version} - slf4j-log4j12 org.slf4j + slf4j-log4j12 @@ -200,8 +199,8 @@ ${project.version} - slf4j-log4j12 org.slf4j + slf4j-log4j12 @@ -240,4 +239,4 @@ cron-utils - \ No newline at end of file + diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/SeatunnelApplication.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/SeatunnelApplication.java index 4947195fe..967167890 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/SeatunnelApplication.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/SeatunnelApplication.java @@ -25,7 +25,8 @@ import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; -@SpringBootApplication(scanBasePackages = {"org.apache.seatunnel.app", "org.apache.seatunnel.scheduler"}) +@SpringBootApplication( + scanBasePackages = {"org.apache.seatunnel.app", "org.apache.seatunnel.scheduler"}) @EnableTransactionManagement @EnableConfigurationProperties @EnableScheduling diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java index 84117d8ef..a20f5a8f0 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/adapter/SeatunnelWebAdapter.java @@ -48,8 +48,7 @@ public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } - @Resource - private UserIdMethodArgumentResolver currentUserMethodArgumentResolver; + @Resource private UserIdMethodArgumentResolver currentUserMethodArgumentResolver; /** * Cookie @@ -70,10 +69,18 @@ public LocaleResolver localeResolver() { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) - .order(1) - .addPathPatterns(LOGIN_INTERCEPTOR_PATH_PATTERN) - .excludePathPatterns(LOGIN_PATH_PATTERN, REGISTER_PATH_PATTERN, - "/swagger-resources/**", "/webjars/**", "/v2/**", "*.html", "/ui/**", "/error", "/swagger-ui.html**"); + .order(1) + .addPathPatterns(LOGIN_INTERCEPTOR_PATH_PATTERN) + .excludePathPatterns( + LOGIN_PATH_PATTERN, + REGISTER_PATH_PATTERN, + "/swagger-resources/**", + "/webjars/**", + "/v2/**", + "*.html", + "/ui/**", + "/error", + "/swagger-ui.html**"); } @Override @@ -84,7 +91,8 @@ public void addArgumentResolvers(List argumentRes @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); - registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + registry.addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/"); registry.addResourceHandler("/ui/**").addResourceLocations("file:ui/"); } @@ -93,5 +101,4 @@ public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("redirect:/ui/"); registry.addViewController("/ui/").setViewName("forward:/ui/index.html"); } - } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/LogoutAspect.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/LogoutAspect.java index 3d481c2f5..48c7b6769 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/LogoutAspect.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/LogoutAspect.java @@ -17,11 +17,8 @@ package org.apache.seatunnel.app.aspect; -import static org.apache.seatunnel.server.common.Constants.USER_ID; - import org.apache.seatunnel.app.dal.dao.IUserDao; -import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @@ -31,26 +28,28 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import lombok.extern.slf4j.Slf4j; + import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import static org.apache.seatunnel.server.common.Constants.USER_ID; + @Slf4j @Aspect @Component @Order(2) public class LogoutAspect { - @Resource - private IUserDao userDaoImpl; + @Resource private IUserDao userDaoImpl; @Pointcut("execution(public * org.apache.seatunnel.app.controller.UserController.logout(..))") - public void logoutPointCut() { - - } + public void logoutPointCut() {} @Before("logoutPointCut()") public void check(JoinPoint pjp) { - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + ServletRequestAttributes attributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); final Integer userId = (Integer) request.getAttribute(USER_ID); userDaoImpl.disableToken(userId); diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/UserId.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/UserId.java index 628e103ca..8d812be39 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/UserId.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/aspect/UserId.java @@ -24,5 +24,4 @@ @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) -public @interface UserId { -} +public @interface UserId {} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/RoleTypeEnum.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/RoleTypeEnum.java index cd2566abc..836bfe7e4 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/RoleTypeEnum.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/RoleTypeEnum.java @@ -23,7 +23,7 @@ public enum RoleTypeEnum { ; private final int code; - private final String description; + private final String description; RoleTypeEnum(int code, String description) { this.code = code; diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptParamStatusEnum.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptParamStatusEnum.java index 56195dd8d..22f286be2 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptParamStatusEnum.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptParamStatusEnum.java @@ -23,7 +23,7 @@ public enum ScriptParamStatusEnum { ; private final int code; - private final String description; + private final String description; ScriptParamStatusEnum(int code, String description) { this.code = code; diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptStatusEnum.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptStatusEnum.java index 0a4144e04..154fd2a5b 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptStatusEnum.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptStatusEnum.java @@ -24,7 +24,7 @@ public enum ScriptStatusEnum { ; private final int code; - private final String description; + private final String description; ScriptStatusEnum(int code, String description) { this.code = code; diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptTypeEnum.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptTypeEnum.java index 89d627c60..353f2b2c3 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptTypeEnum.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/ScriptTypeEnum.java @@ -23,7 +23,7 @@ public enum ScriptTypeEnum { ; private final int code; - private final String description; + private final String description; ScriptTypeEnum(int code, String description) { this.code = code; diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java index 96e4780b9..aa3144bec 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/SeaTunnelConnectorI18n.java @@ -30,13 +30,18 @@ public class SeaTunnelConnectorI18n { static { try { - CONNECTOR_I18N_CONFIG_EN = ConfigFactory.parseString(IOUtils.toString(SeaTunnelConnectorI18n.class.getResourceAsStream("/i18n_en.config"), StandardCharsets.UTF_8)); + CONNECTOR_I18N_CONFIG_EN = + ConfigFactory.parseString( + IOUtils.toString( + SeaTunnelConnectorI18n.class.getResourceAsStream( + "/i18n_en.config"), + StandardCharsets.UTF_8)); CONNECTOR_I18N_CONFIG_ZH = - ConfigFactory.parseString( - IOUtils.toString( - SeaTunnelConnectorI18n.class.getResourceAsStream( - "/i18n_zh.config"), - StandardCharsets.UTF_8)); + ConfigFactory.parseString( + IOUtils.toString( + SeaTunnelConnectorI18n.class.getResourceAsStream( + "/i18n_zh.config"), + StandardCharsets.UTF_8)); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserStatusEnum.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserStatusEnum.java index c62b7e1cf..e8d533d17 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserStatusEnum.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserStatusEnum.java @@ -22,7 +22,7 @@ public enum UserStatusEnum { DISABLE(1, "disable"), ; private final int code; - private final String description; + private final String description; UserStatusEnum(int code, String description) { this.code = code; diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserTypeEnum.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserTypeEnum.java index b583e6f48..6b8f3dfb2 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserTypeEnum.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/UserTypeEnum.java @@ -23,7 +23,7 @@ public enum UserTypeEnum { ; private final int code; - private final String description; + private final String description; UserTypeEnum(int code, String description) { this.code = code; diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/Swagger2.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/Swagger2.java index 3eb93a856..8e6df8543 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/Swagger2.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/Swagger2.java @@ -19,6 +19,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; + import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; @@ -29,7 +30,7 @@ @Configuration @EnableSwagger2 -public class Swagger2{ +public class Swagger2 { @Bean public Docket createRestApi() { @@ -49,5 +50,4 @@ private ApiInfo apiInfo() { .termsOfServiceUrl("https://seatunnel.apache.org/") .build(); } - } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java index 39ab914ca..091190b04 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/AuthController.java @@ -20,14 +20,15 @@ import org.apache.seatunnel.app.common.Result; import org.apache.seatunnel.app.service.IRoleService; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; + import javax.annotation.Resource; import javax.validation.constraints.NotNull; @@ -35,8 +36,7 @@ @RestController public class AuthController { - @Resource - private IRoleService roleServiceImpl; + @Resource private IRoleService roleServiceImpl; @GetMapping("/userRole") @ApiOperation(value = "check relation between user and role", httpMethod = "GET") @@ -44,7 +44,9 @@ public class AuthController { @ApiImplicitParam(name = "username", value = "user name", dataType = "String"), @ApiImplicitParam(name = "roleName", value = "role name", dataType = "String"), }) - public Result userRole(@RequestParam("username") @NotNull String username, @RequestParam("roleName") @NotNull String roleName) { + public Result userRole( + @RequestParam("username") @NotNull String username, + @RequestParam("roleName") @NotNull String roleName) { final boolean b = roleServiceImpl.checkUserRole(username, roleName); return Result.success(b); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java index e1885e295..bf22b5a54 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/ScriptController.java @@ -31,8 +31,6 @@ import org.apache.seatunnel.app.domain.response.script.ScriptSimpleInfoRes; import org.apache.seatunnel.app.service.IScriptService; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -43,6 +41,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import springfox.documentation.annotations.ApiIgnore; import javax.annotation.Resource; @@ -53,22 +54,24 @@ @RequestMapping("/seatunnel/api/v1/script") @RestController public class ScriptController { - @Resource - private IScriptService iScriptService; + @Resource private IScriptService iScriptService; @PostMapping @ApiOperation(value = "add an script with content", httpMethod = "POST") - public Result createScript(@RequestBody @NotNull CreateScriptReq createScriptReq, - @ApiIgnore @UserId Integer operatorId) { + public Result createScript( + @RequestBody @NotNull CreateScriptReq createScriptReq, + @ApiIgnore @UserId Integer operatorId) { createScriptReq.setCreatorId(operatorId); return Result.success(iScriptService.createScript(createScriptReq)); } @PutMapping("/{scriptId}/content") @ApiOperation(value = "update script", httpMethod = "PUT") - public Result updateScriptContent(@ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") Integer scriptId, - @RequestBody @NotNull UpdateScriptContentReq req, - @ApiIgnore @UserId Integer operatorId) { + public Result updateScriptContent( + @ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") + Integer scriptId, + @RequestBody @NotNull UpdateScriptContentReq req, + @ApiIgnore @UserId Integer operatorId) { req.setScriptId(scriptId); req.setMenderId(operatorId); @@ -78,17 +81,20 @@ public Result updateScriptContent(@ApiParam(value = "script id", required @DeleteMapping("/{scriptId}") @ApiOperation(value = "delete script", httpMethod = "DELETE") - public Result delete(@ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") Integer scriptId) { + public Result delete( + @ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") + Integer scriptId) { iScriptService.delete(scriptId); return Result.success(); } @GetMapping @ApiOperation(value = "script list", httpMethod = "GET") - public Result> list(@ApiParam(value = "script name") @RequestParam(required = false) String name, - @ApiParam(value = "script status") @RequestParam(required = false) Byte status, - @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, - @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { + public Result> list( + @ApiParam(value = "script name") @RequestParam(required = false) String name, + @ApiParam(value = "script status") @RequestParam(required = false) Byte status, + @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, + @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { final ScriptListReq req = new ScriptListReq(); req.setName(name); @@ -100,19 +106,25 @@ public Result> list(@ApiParam(value = "script name @GetMapping("/{scriptId}/content") @ApiOperation(value = "fetch script content", httpMethod = "GET") - public Result fetchScriptContent(@ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") Integer scriptId) { + public Result fetchScriptContent( + @ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") + Integer scriptId) { return Result.success(iScriptService.fetchScriptContent(scriptId)); } @GetMapping("/{scriptId}") - public Result detail(@ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") Integer scriptId) { + public Result detail( + @ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") + Integer scriptId) { return Result.success(iScriptService.detail(scriptId)); } @PutMapping("/{scriptId}/param") @ApiOperation(value = "update script param", httpMethod = "PUT") - public Result updateScriptParam(@ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") Integer scriptId, - @RequestBody @NotNull UpdateScriptParamReq updateScriptParamReq) { + public Result updateScriptParam( + @ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") + Integer scriptId, + @RequestBody @NotNull UpdateScriptParamReq updateScriptParamReq) { updateScriptParamReq.setScriptId(scriptId); iScriptService.updateScriptParam(updateScriptParamReq); return Result.success(); @@ -120,14 +132,18 @@ public Result updateScriptParam(@ApiParam(value = "script id", required = @GetMapping("/{scriptId}/param") @ApiOperation(value = "fetch script param", httpMethod = "GET") - public Result> fetchScriptParam(@ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") Integer scriptId) { + public Result> fetchScriptParam( + @ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") + Integer scriptId) { return Result.success(iScriptService.fetchScriptParam(scriptId)); } @PatchMapping("/{scriptId}/publish") @ApiOperation(value = "publish script", httpMethod = "PATCH") - public Result publish(@ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") Integer scriptId, - @ApiIgnore @UserId Integer operatorId) { + public Result publish( + @ApiParam(value = "script id", required = true) @PathVariable(value = "scriptId") + Integer scriptId, + @ApiIgnore @UserId Integer operatorId) { final PublishScriptReq req = new PublishScriptReq(); req.setScriptId(scriptId); diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java index b66de54dc..7fc63526d 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/TaskController.java @@ -29,8 +29,6 @@ import org.apache.seatunnel.app.domain.response.task.JobSimpleInfoRes; import org.apache.seatunnel.app.service.ITaskService; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -39,6 +37,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import springfox.documentation.annotations.ApiIgnore; import javax.annotation.Resource; @@ -48,13 +49,13 @@ @RestController public class TaskController { - @Resource - private ITaskService iTaskService; + @Resource private ITaskService iTaskService; @PatchMapping("/{jobId}/recycle") @ApiOperation(value = "recycle job", httpMethod = "PATCH") - Result recycle(@ApiParam(value = "job id", required = true) @PathVariable(value = "jobId") Long jobId, - @ApiIgnore @UserId Integer operatorId) { + Result recycle( + @ApiParam(value = "job id", required = true) @PathVariable(value = "jobId") Long jobId, + @ApiIgnore @UserId Integer operatorId) { final RecycleScriptReq req = new RecycleScriptReq(); req.setJobId(jobId); req.setOperatorId(operatorId); @@ -65,9 +66,10 @@ Result recycle(@ApiParam(value = "job id", required = true) @PathVariable( @GetMapping("/job") @ApiOperation(value = "list job", httpMethod = "GET") - Result> listJob(@ApiParam(value = "job name") @RequestParam(required = false) String name, - @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, - @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { + Result> listJob( + @ApiParam(value = "job name") @RequestParam(required = false) String name, + @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, + @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { final JobListReq req = new JobListReq(); req.setName(name); req.setPageNo(pageNo); @@ -78,9 +80,11 @@ Result> listJob(@ApiParam(value = "job name") @Reques @GetMapping("/instance") @ApiOperation(value = "list instance", httpMethod = "GET") - Result> listInstance(@ApiParam(value = "job name", required = false) @RequestParam(required = false) String name, - @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, - @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { + Result> listInstance( + @ApiParam(value = "job name", required = false) @RequestParam(required = false) + String name, + @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, + @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { final InstanceListReq req = new InstanceListReq(); req.setName(name); req.setPageNo(pageNo); @@ -91,9 +95,11 @@ Result> listInstance(@ApiParam(value = "job name @PostMapping("/{objectId}/execute") @ApiOperation(value = "execute script temporary", httpMethod = "POST") - Result tmpExecute(@ApiParam(value = "object id", required = true) @PathVariable(value = "objectId") Long objectId, - @RequestBody @NotNull ExecuteReq req, - @ApiIgnore @UserId Integer operatorId) { + Result tmpExecute( + @ApiParam(value = "object id", required = true) @PathVariable(value = "objectId") + Long objectId, + @RequestBody @NotNull ExecuteReq req, + @ApiIgnore @UserId Integer operatorId) { req.setObjectId(objectId); req.setOperatorId(operatorId); @@ -102,13 +108,19 @@ Result tmpExecute(@ApiParam(value = "object id", required @GetMapping("/{taskInstanceId}") @ApiOperation(value = "query instance log", httpMethod = "GET") - Result queryInstanceLog(@ApiParam(value = "task instance id", required = true) @PathVariable(value = "taskInstanceId") Long taskInstanceId) { + Result queryInstanceLog( + @ApiParam(value = "task instance id", required = true) + @PathVariable(value = "taskInstanceId") + Long taskInstanceId) { return Result.success(iTaskService.queryInstanceLog(taskInstanceId)); } @PatchMapping("/{taskInstanceId}") @ApiOperation(value = "kill running instance", httpMethod = "POST") - Result kill(@ApiParam(value = "task instance id", required = true) @PathVariable(value = "taskInstanceId") Long taskInstanceId) { + Result kill( + @ApiParam(value = "task instance id", required = true) + @PathVariable(value = "taskInstanceId") + Long taskInstanceId) { iTaskService.kill(taskInstanceId); return Result.success(); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java index 6a818496a..106f23ff9 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java @@ -27,8 +27,6 @@ import org.apache.seatunnel.app.domain.response.user.UserSimpleInfoRes; import org.apache.seatunnel.app.service.IUserService; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -40,6 +38,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + import javax.annotation.Resource; import javax.validation.constraints.NotNull; @@ -47,8 +48,7 @@ @RestController public class UserController { - @Resource - private IUserService iUserService; + @Resource private IUserService iUserService; @PostMapping @ApiOperation(value = "add user", httpMethod = "POST") @@ -58,8 +58,10 @@ public Result add(@RequestBody @NotNull AddUserReq addReq) { @PutMapping("/{userId}") @ApiOperation(value = "update user", httpMethod = "PUT") - public Result update(@ApiParam(value = "user id", required = true) @PathVariable(value = "userId") Integer userId, - @RequestBody @NotNull UpdateUserReq updateReq) { + public Result update( + @ApiParam(value = "user id", required = true) @PathVariable(value = "userId") + Integer userId, + @RequestBody @NotNull UpdateUserReq updateReq) { updateReq.setUserId(userId); iUserService.update(updateReq); @@ -68,16 +70,19 @@ public Result update(@ApiParam(value = "user id", required = true) @PathVa @DeleteMapping("/{userId}") @ApiOperation(value = "delete user", httpMethod = "DELETE") - public Result delete(@ApiParam(value = "user id", required = true) @PathVariable(value = "userId") Integer userId) { + public Result delete( + @ApiParam(value = "user id", required = true) @PathVariable(value = "userId") + Integer userId) { iUserService.delete(userId); return Result.success(); } @GetMapping @ApiOperation(value = "user list", httpMethod = "GET") - public Result> list(@ApiParam(value = "user name") @RequestParam(required = false) String name, - @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, - @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { + public Result> list( + @ApiParam(value = "user name") @RequestParam(required = false) String name, + @ApiParam(value = "page num", required = true) @RequestParam Integer pageNo, + @ApiParam(value = "page size", required = true) @RequestParam Integer pageSize) { final UserListReq req = new UserListReq(); req.setName(name); req.setPageNo(pageNo); @@ -88,14 +93,18 @@ public Result> list(@ApiParam(value = "user name") @ @PatchMapping("/{userId}/enable") @ApiOperation(value = "enable a user", httpMethod = "PATCH") - public Result enable(@ApiParam(value = "user id", required = true) @PathVariable(value = "userId") Integer userId) { + public Result enable( + @ApiParam(value = "user id", required = true) @PathVariable(value = "userId") + Integer userId) { iUserService.enable(userId); return Result.success(); } @PutMapping("/{userId}/disable") @ApiOperation(value = "disable a user", httpMethod = "PUT") - public Result disable(@ApiParam(value = "user id", required = true) @PathVariable(value = "userId") Integer userId) { + public Result disable( + @ApiParam(value = "user id", required = true) @PathVariable(value = "userId") + Integer userId) { iUserService.disable(userId); return Result.success(); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleDaoImpl.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleDaoImpl.java index ae0b32629..227f8f2b4 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleDaoImpl.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleDaoImpl.java @@ -28,11 +28,10 @@ @Repository public class RoleDaoImpl implements IRoleDao { - @Resource - private RoleMapper roleMapper; + @Resource private RoleMapper roleMapper; @Override - public void add(Role role){ + public void add(Role role) { roleMapper.insert(role); } @@ -40,5 +39,4 @@ public void add(Role role){ public Role getByRoleName(String roleName) { return roleMapper.selectByRole(roleName); } - } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleUserRelationDaoImpl.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleUserRelationDaoImpl.java index 8bc66ddce..bee2cd7b3 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleUserRelationDaoImpl.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/RoleUserRelationDaoImpl.java @@ -28,17 +28,17 @@ @Repository public class RoleUserRelationDaoImpl implements IRoleUserRelationDao { - @Resource - private RoleUserRelationMapper roleUserRelationMapper; + @Resource private RoleUserRelationMapper roleUserRelationMapper; @Override - public void add(RoleUserRelation roleUserRelation){ + public void add(RoleUserRelation roleUserRelation) { roleUserRelationMapper.insert(roleUserRelation); } @Override public RoleUserRelation getByUserAndRole(Integer userId, Integer roleId) { - final RoleUserRelation roleUserRelation = roleUserRelationMapper.selectByUserIdAndRoleId(userId, roleId); + final RoleUserRelation roleUserRelation = + roleUserRelationMapper.selectByUserIdAndRoleId(userId, roleId); return roleUserRelation; } @@ -46,5 +46,4 @@ public RoleUserRelation getByUserAndRole(Integer userId, Integer roleId) { public void deleteByUserId(Integer userId) { roleUserRelationMapper.deleteByUserId(userId); } - } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/SchedulerConfigDaoImpl.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/SchedulerConfigDaoImpl.java index fa78042bd..1d6ac1fdd 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/SchedulerConfigDaoImpl.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/SchedulerConfigDaoImpl.java @@ -29,8 +29,7 @@ @Repository public class SchedulerConfigDaoImpl implements ISchedulerConfigDao { - @Resource - private SchedulerConfigMapper schedulerConfigMapper; + @Resource private SchedulerConfigMapper schedulerConfigMapper; @Override public boolean exists(int scriptId) { diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/ScriptDaoImpl.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/ScriptDaoImpl.java index 7f18048d8..9e2ebdc5f 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/ScriptDaoImpl.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/ScriptDaoImpl.java @@ -17,9 +17,6 @@ package org.apache.seatunnel.app.dal.dao.impl; -import static org.apache.seatunnel.server.common.SeatunnelErrorEnum.SCRIPT_ALREADY_EXIST; -import static com.google.common.base.Preconditions.checkState; - import org.apache.seatunnel.app.common.ScriptStatusEnum; import org.apache.seatunnel.app.dal.dao.IScriptDao; import org.apache.seatunnel.app.dal.entity.Script; @@ -37,16 +34,24 @@ import java.util.List; import java.util.Objects; +import static com.google.common.base.Preconditions.checkState; +import static org.apache.seatunnel.server.common.SeatunnelErrorEnum.SCRIPT_ALREADY_EXIST; + @Repository public class ScriptDaoImpl implements IScriptDao { - @Resource - private ScriptMapper scriptMapper; + @Resource private ScriptMapper scriptMapper; @Override public void checkScriptDuplicate(CheckScriptDuplicateDto dto) { - final Script script = scriptMapper.selectByNameAndCreatorAndStatusNotEq(dto.getName(), dto.getCreatorId(), (byte) ScriptStatusEnum.DELETED.getCode()); - checkState(Objects.isNull(script), String.format(SCRIPT_ALREADY_EXIST.getTemplate(), dto.getName())); + final Script script = + scriptMapper.selectByNameAndCreatorAndStatusNotEq( + dto.getName(), + dto.getCreatorId(), + (byte) ScriptStatusEnum.DELETED.getCode()); + checkState( + Objects.isNull(script), + String.format(SCRIPT_ALREADY_EXIST.getTemplate(), dto.getName())); } @Override @@ -69,7 +74,8 @@ public Script getScript(Integer id) { @Override public void updateScriptContent(UpdateScriptContentDto dto) { - scriptMapper.updateContentByPrimaryKey(dto.getId(), dto.getContent(), dto.getContentMd5(), dto.getMenderId()); + scriptMapper.updateContentByPrimaryKey( + dto.getId(), dto.getContent(), dto.getContentMd5(), dto.getMenderId()); } @Override @@ -82,7 +88,8 @@ public PageData