diff --git a/lib/datasource/maven/index.ts b/lib/datasource/maven/index.ts
index 5867caa41ca61c2e56e32cec77685db86c0d2924..8ee4e527b475dfb2f2e93f183047fa5e7c023335 100644
--- a/lib/datasource/maven/index.ts
+++ b/lib/datasource/maven/index.ts
@@ -1,5 +1,4 @@
 import url from 'url';
-import fs from 'fs-extra';
 import pAll from 'p-all';
 import { XmlDocument } from 'xmldoc';
 import { logger } from '../../logger';
@@ -9,7 +8,14 @@ import * as mavenVersioning from '../../versioning/maven';
 import { compare } from '../../versioning/maven/compare';
 import type { GetReleasesConfig, Release, ReleaseResult } from '../types';
 import { MAVEN_REPO } from './common';
-import { downloadHttpProtocol, isHttpResourceExists } from './util';
+import type { MavenDependency } from './types';
+import {
+  downloadMavenXml,
+  getDependencyInfo,
+  getDependencyParts,
+  getMavenUrl,
+  isHttpResourceExists,
+} from './util';
 
 export { id } from './common';
 
@@ -18,93 +24,6 @@ export const defaultRegistryUrls = [MAVEN_REPO];
 export const defaultVersioning = mavenVersioning.id;
 export const registryStrategy = 'merge';
 
-function containsPlaceholder(str: string): boolean {
-  return /\${.*?}/g.test(str);
-}
-
-async function downloadFileProtocol(pkgUrl: url.URL): Promise<string | null> {
-  const pkgPath = pkgUrl.toString().replace('file://', '');
-  if (!(await fs.exists(pkgPath))) {
-    return null;
-  }
-  return fs.readFile(pkgPath, 'utf8');
-}
-
-function getMavenUrl(
-  dependency: MavenDependency,
-  repoUrl: string,
-  path: string
-): url.URL | null {
-  return new url.URL(`${dependency.dependencyUrl}/${path}`, repoUrl);
-}
-
-interface MavenXml {
-  authorization?: boolean;
-  xml?: XmlDocument;
-}
-
-async function downloadMavenXml(
-  pkgUrl: url.URL | null
-): Promise<MavenXml | null> {
-  /* istanbul ignore if */
-  if (!pkgUrl) {
-    return {};
-  }
-  let rawContent: string;
-  let authorization: boolean;
-  switch (pkgUrl.protocol) {
-    case 'file:':
-      rawContent = await downloadFileProtocol(pkgUrl);
-      break;
-    case 'http:':
-    case 'https:':
-      ({ authorization, body: rawContent } = await downloadHttpProtocol(
-        pkgUrl
-      ));
-      break;
-    case 's3:':
-      logger.debug('Skipping s3 dependency');
-      return {};
-    default:
-      logger.debug({ url: pkgUrl.toString() }, `Unsupported Maven protocol`);
-      return {};
-  }
-
-  if (!rawContent) {
-    logger.debug(`Content is not found for Maven url: ${pkgUrl.toString()}`);
-    return {};
-  }
-
-  return { authorization, xml: new XmlDocument(rawContent) };
-}
-
-async function getDependencyInfo(
-  dependency: MavenDependency,
-  repoUrl: string,
-  version: string
-): Promise<Partial<ReleaseResult>> {
-  const result: Partial<ReleaseResult> = {};
-  const path = `${version}/${dependency.name}-${version}.pom`;
-
-  const pomUrl = getMavenUrl(dependency, repoUrl, path);
-  const { xml: pomContent } = await downloadMavenXml(pomUrl);
-  if (!pomContent) {
-    return result;
-  }
-
-  const homepage = pomContent.valueWithPath('url');
-  if (homepage && !containsPlaceholder(homepage)) {
-    result.homepage = homepage;
-  }
-
-  const sourceUrl = pomContent.valueWithPath('scm.url');
-  if (sourceUrl && !containsPlaceholder(sourceUrl)) {
-    result.sourceUrl = sourceUrl.replace(/^scm:/, '');
-  }
-
-  return result;
-}
-
 function isStableVersion(x: string): boolean {
   return mavenVersion.isStable(x);
 }
@@ -121,24 +40,6 @@ function getLatestStableVersion(releases: Release[]): string | null {
   return null;
 }
 
-interface MavenDependency {
-  display: string;
-  group?: string;
-  name?: string;
-  dependencyUrl: string;
-}
-
-function getDependencyParts(lookupName: string): MavenDependency {
-  const [group, name] = lookupName.split(':');
-  const dependencyUrl = `${group.replace(/\./g, '/')}/${name}`;
-  return {
-    display: lookupName,
-    group,
-    name,
-    dependencyUrl,
-  };
-}
-
 function extractVersions(metadata: XmlDocument): string[] {
   const versions = metadata.descendantWithPath('versioning.versions');
   const elements = versions?.childrenNamed('version');
diff --git a/lib/datasource/maven/types.ts b/lib/datasource/maven/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06d8fd4c211d262a4c502da23c3dd1ca5c693403
--- /dev/null
+++ b/lib/datasource/maven/types.ts
@@ -0,0 +1,13 @@
+import type { XmlDocument } from 'xmldoc';
+
+export interface MavenDependency {
+  display: string;
+  group?: string;
+  name?: string;
+  dependencyUrl: string;
+}
+
+export interface MavenXml {
+  authorization?: boolean;
+  xml?: XmlDocument;
+}
diff --git a/lib/datasource/maven/util.ts b/lib/datasource/maven/util.ts
index 335634caac07496c80be1db1067e281277a4b763..311c1041d4374d39a74fd7f671f4f4f71761de78 100644
--- a/lib/datasource/maven/util.ts
+++ b/lib/datasource/maven/util.ts
@@ -1,10 +1,14 @@
 import url from 'url';
+import fs from 'fs-extra';
+import { XmlDocument } from 'xmldoc';
 import { HOST_DISABLED } from '../../constants/error-messages';
 import { logger } from '../../logger';
 import { ExternalHostError } from '../../types/errors/external-host-error';
 import { Http, HttpResponse } from '../../util/http';
 
+import type { ReleaseResult } from '../types';
 import { MAVEN_REPO, id } from './common';
+import type { MavenDependency, MavenXml } from './types';
 
 const http: Record<string, Http> = {};
 
@@ -96,6 +100,14 @@ export async function downloadHttpProtocol(
   }
 }
 
+async function downloadFileProtocol(pkgUrl: url.URL): Promise<string | null> {
+  const pkgPath = pkgUrl.toString().replace('file://', '');
+  if (!(await fs.exists(pkgPath))) {
+    return null;
+  }
+  return fs.readFile(pkgPath, 'utf8');
+}
+
 export async function isHttpResourceExists(
   pkgUrl: url.URL | string,
   hostType = id
@@ -119,3 +131,88 @@ export async function isHttpResourceExists(
     return null;
   }
 }
+
+function containsPlaceholder(str: string): boolean {
+  return /\${.*?}/g.test(str);
+}
+
+export function getMavenUrl(
+  dependency: MavenDependency,
+  repoUrl: string,
+  path: string
+): url.URL | null {
+  return new url.URL(`${dependency.dependencyUrl}/${path}`, repoUrl);
+}
+
+export async function downloadMavenXml(
+  pkgUrl: url.URL | null
+): Promise<MavenXml | null> {
+  /* istanbul ignore if */
+  if (!pkgUrl) {
+    return {};
+  }
+  let rawContent: string;
+  let authorization: boolean;
+  switch (pkgUrl.protocol) {
+    case 'file:':
+      rawContent = await downloadFileProtocol(pkgUrl);
+      break;
+    case 'http:':
+    case 'https:':
+      ({ authorization, body: rawContent } = await downloadHttpProtocol(
+        pkgUrl
+      ));
+      break;
+    case 's3:':
+      logger.debug('Skipping s3 dependency');
+      return {};
+    default:
+      logger.debug({ url: pkgUrl.toString() }, `Unsupported Maven protocol`);
+      return {};
+  }
+
+  if (!rawContent) {
+    logger.debug(`Content is not found for Maven url: ${pkgUrl.toString()}`);
+    return {};
+  }
+
+  return { authorization, xml: new XmlDocument(rawContent) };
+}
+
+export async function getDependencyInfo(
+  dependency: MavenDependency,
+  repoUrl: string,
+  version: string
+): Promise<Partial<ReleaseResult>> {
+  const result: Partial<ReleaseResult> = {};
+  const path = `${version}/${dependency.name}-${version}.pom`;
+
+  const pomUrl = getMavenUrl(dependency, repoUrl, path);
+  const { xml: pomContent } = await downloadMavenXml(pomUrl);
+  if (!pomContent) {
+    return result;
+  }
+
+  const homepage = pomContent.valueWithPath('url');
+  if (homepage && !containsPlaceholder(homepage)) {
+    result.homepage = homepage;
+  }
+
+  const sourceUrl = pomContent.valueWithPath('scm.url');
+  if (sourceUrl && !containsPlaceholder(sourceUrl)) {
+    result.sourceUrl = sourceUrl.replace(/^scm:/, '');
+  }
+
+  return result;
+}
+
+export function getDependencyParts(lookupName: string): MavenDependency {
+  const [group, name] = lookupName.split(':');
+  const dependencyUrl = `${group.replace(/\./g, '/')}/${name}`;
+  return {
+    display: lookupName,
+    group,
+    name,
+    dependencyUrl,
+  };
+}