简体   繁体   中英

Make the web application auto-determine and know its version

My team are doing with 2 teams: server team and client team. We at server team will provide APIs for the client team to call. The APIs have many version, so that we need to match the server build and the respective client build - for example, the old client will refuse to work if the server build number is much larger than its support version and requires an update.

Because of the above reason, we need to send back the build version of server to client. Currently we are doing this by adding a static field in a Config class. But I'm concerned with the fact that we must manually increase it everytime a new server is built - especially when we do daily build. This process is quite error-prone and not quite elegant.

In my search, I see many propose for using maven plugins to manage the build version. Though I highly appreciated the auto-process, it still doesn't let the server know the build number. Server application should be able to return its build version to client through an API call.

I have thought of write the number version somewhere (remote database, files on server). Is there any way to make the building process automatically increase the build version, but the application itself can retrieve this number in running also?

We are using Maven build and having Jenkin as the integration build server.

I usually read the version from the MANIFEST.MF file that is packaged in the JAR by Maven. By default it looks something like this:

Manifest-Version: 1.0
Built-By: user
Build-Jdk: 1.6.0_35
Created-By: Apache Maven 3.0.4
Archiver-Version: Plexus Archiver

Name: Artifact
Implementation-Build: 1.14-SNAPSHOT
Version: 1.14-SNAPSHOT

From this file I read the Version element and use that to for instance display the build version of the application (and all versions of the packaged jars in a WAR/EAR for instance). Something like this code should work:

public static String getApplicationVersion() {
        String version = null;
        try {
            final List<VersionsUtil.Version> moduleVersions = VersionsUtil.getModuleVersions(Thread.currentThread().getContextClassLoader());
            for (VersionsUtil.Version moduleVersion : moduleVersions) {
                if (moduleVersion.name.equals("<NAME OF ARTIFACT TO GET>")) {
                    version = moduleVersion.version;
                    break;
                }
            }
        } catch (IOException e) {
            // We'll return null...
        }
        return version;
    }


public class VersionsUtil {
    private static final Logger LOG = LoggerFactory.getLogger(VersionsUtil.class);

    /**
     * Returns a list of the module versions available for the given class loader.
     *
     * @param classLoader the class loader to return module versions for
     * @return a list of module versions
     * @throws IOException in case there's an error reading the manifest
     */
    public static List<Version> getModuleVersions(final ClassLoader classLoader) throws IOException {
        return processResources(classLoader.getResources("META-INF/MANIFEST.MF"));
    }

    private static List<Version> processResources(final Enumeration<URL> resources) throws IOException {
        final List<Version> moduleVersions = new ArrayList();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            Version v = process(resource);
            if (v != null) {
                moduleVersions.add(v);
            }
        }
        return moduleVersions;
    }

    private static Version process(final URL resource) {
        try {
            Properties p = readResource(resource);
            return createVersion(p);
        } catch (IOException e) {
            LOG.warn("Failed to read resource: " + resource, e);
            return null;
        }
    }

    private static Version createVersion(final Properties p) {
        Object name = p.get("Name");
        if (name != null) {
            return new Version((String) name, (String) p.get("Version"));
        }
        return null;
    }

    private static Properties readResource(final URL resource) throws IOException {
        LOG.trace("Reading resource: " + resource);
        InputStream is = resource.openStream();
        Properties p = new Properties();
        p.load(is);
        is.close();
        return p;
    }

    public static final class Version {
        String name;
        String version;

        private Version(final String name, final String version) {
            this.name = name;
            this.version = version;
        }

    }
}

Updated: If you want Jenkins buildnumber in the MANIFEST.MF you can configure your POM.XML with something like:

  ...
  <plugin>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.2</version>
    <configuration>
      <archive>
        <manifestSections>
          <manifestSection>
            <name>${project.name} (${project.artifactId})</name>
            <manifestEntries>
              <Version>${project.version}${build.number}</Version>
            </manifestEntries>
          </manifestSection>
        </manifestSections>
      </archive>
    </configuration>
  </plugin>
  ...
  <properties>
     <build.number />
  </properties>
  ...

If you're interested in tagging the WAR/EAR files instead, you have to add the manifest configurations accordingly. Then in your Jenkins job configuration, simply pass the BUILD_NUMBER parameter to the maven process like this: -Dbuild.number=$BUILD_NUMBER .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM