001package com.randomnoun.common.servlet;
002
003/* (c) 2013-2016 randomnoun. All Rights Reserved. This work is licensed under a
004 * BSD Simplified License. (http://www.randomnoun.com/bsd-simplified.html)
005 */
006
007import java.io.File;
008import java.io.FileInputStream;
009import java.io.IOException;
010import java.io.InputStream;
011import java.util.Properties;
012
013import javax.servlet.ServletException;
014import javax.servlet.http.HttpServlet;
015import javax.servlet.http.HttpServletRequest;
016import javax.servlet.http.HttpServletResponse;
017
018import org.apache.log4j.Logger;
019import org.json.JSONObject;
020
021/**
022 * Reads the contents of the build.properties file in the 
023 * current application and sends it to the browser in a 
024 * variety of forms. Probably JSON for starters.
025 *
026 * <p>The properties file must be located on the classpath at the
027 * location "/build.properties".
028 * 
029 * <p>For this to work, the application needs to have been built
030 * through maven (and possibly bamboo), and there should be a 
031 * <code>build.properties</code> file in the application being created, 
032 * with the contents:
033 * 
034 * <pre>
035 *  bamboo.buildKey=${bambooBuildKey}
036        bamboo.buildNumber=${bambooBuildNumber}
037        bamboo.buildPlanName=${bambooBuildPlanName}
038        bamboo.buildTimeStamp=${bambooBuildTimeStamp}
039        bamboo.buildForceCleanCheckout=${bambooForceCleanCheckout}
040        bamboo.custom.cvs.last.update.time=${bambooCustomCvsLastUpdateTime}
041        bamboo.custom.cvs.last.update.time.label=${bambooCustomCvsLastUpdateTimeLabel}
042        
043        maven.pom.name=${pom.name}
044    maven.pom.version=${pom.version}
045    maven.pom.groupId=${pom.groupId}
046    maven.pom.artifactId=${pom.artifactId}
047   </pre>
048 *
049 * and the bamboo plan should have the following in the bamboo plan's
050 * stage's job's task's maven goal specification:
051 * 
052 * <pre>
053 *  "-DbambooBuildKey=${bamboo.buildKey}" 
054    "-DbambooBuildNumber=${bamboo.buildNumber}" 
055    "-DbambooBuildPlanName=${bamboo.buildPlanName}" 
056    "-DbambooBuildTimeStamp=${bamboo.buildTimeStamp}" 
057    "-DbambooBuildForceCleanCheckout=${bamboo.buildForceCleanCheckout}" 
058    "-DbambooCustomCvsLastUpdateTime=${bamboo.custom.cvs.last.update.time}" 
059    "-DbambooCustomCvsLastUpdateTimeLabel=${bamboo.custom.cvs.last.update.time.label}" 
060    -DuniqueVersion=false 
061 * </pre>
062 *
063 * <p>This script will also look for a build.properties file in /etc/build.properties; 
064 * this should exist on docker containers built by bamboo. If this exists, it is also 
065 * included in the JSON output under the "/etc/build.properties" key.
066 * 
067 * 
068 * @author knoxg
069 * @see <a href="http://www.randomnoun.com/wp/2013/09/24/webapp-versions-v1-0/">http://www.randomnoun.com/wp/2013/09/24/webapp-versions-v1-0/</a>
070 * 
071 */
072public class VersionServlet extends HttpServlet {
073    
074        /** Generated serialVersionUID */
075        private static final long serialVersionUID = -6978469440912690523L;
076        
077        /** Logger for this class */
078    public static final Logger logger = Logger.getLogger(VersionServlet.class);
079
080    
081        /** Post method; just defers to get
082         * 
083         * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
084         */
085        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
086                doGet(request, response);
087        }
088
089        /** If a property has not been populated through the maven resource filter
090         * mechanism, then remove it from the Properties object.
091         *   
092         * @param props Properties object
093         * @param key key to check
094         * @param value default value
095         */
096        private void removeDefaultProperty(Properties props, String key, String value) {
097                if (("${" + value + "}").equals(props.get(key))) {
098                        props.remove(key);
099                }
100        }
101        
102        /** Removes build properties that were not set during the build process
103         *  (i.e. are still set to '${xxx}' placeholders).
104         *   
105         * @param props Properties object
106         */
107        private void removeDefaultProperties(Properties props) {
108                // projects not built in bamboo won't set these
109        removeDefaultProperty(props, "bamboo.buildKey", "bambooBuildKey");
110        removeDefaultProperty(props, "bamboo.buildNumber", "bambooBuildNumber");
111        removeDefaultProperty(props, "bamboo.buildPlanName", "bambooBuildPlanName");
112        removeDefaultProperty(props, "bamboo.buildTimeStamp", "bambooBuildTimeStamp");
113        removeDefaultProperty(props, "bamboo.buildForceCleanCheckout", "bambooForceCleanCheckout");
114        // bamboo.custom.cvs.last.update.time == "${bambooCustomCvsLastUpdateTime}" 
115        // if there is no -Dbamboo.custom.cvs.last.update.time property on mvn build cmdline
116        removeDefaultProperty(props, "bamboo.custom.cvs.last.update.time", "bambooCustomCvsLastUpdateTime");
117        removeDefaultProperty(props, "bamboo.custom.cvs.last.update.time.label", "bambooCustomCvsLastUpdateTimeLabel");
118        // bamboo.custom.cvs.last.update.time == "${bamboo.custom.cvs.last.update.time}"
119        // if there IS a -Dbamboo.custom.cvs.last.update.time property on mvn build cmdline
120        // but it's not set by bamboo (because it's now a git project, but the mvn cmdline hasn't been changed)  
121        removeDefaultProperty(props, "bamboo.custom.cvs.last.update.time", "bamboo.custom.cvs.last.update.time");
122        removeDefaultProperty(props, "bamboo.custom.cvs.last.update.time.label", "bamboo.custom.cvs.last.update.time.label");
123        
124        
125        // bamboo git projects have some extra properties we could include here; 
126        //   bamboo.repository.git.branch
127        //   bamboo.repository.git.repositoryUrl
128        // see https://confluence.atlassian.com/bamboo/bamboo-variables-289277087.html
129        
130        // projects in cvs repositories won't set this
131        removeDefaultProperty(props, "git.buildNumber", "buildNumber");
132        }
133        
134        /** See class documentation
135         * 
136         * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
137         */
138    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
139    {
140        InputStream is = VersionServlet.class.getClassLoader().getResourceAsStream("/build.properties");
141        Properties props = new Properties();
142        if (is==null) {
143                props.put("error", "Missing build.properties");
144        } else {
145                props.load(is);
146                is.close();
147        }
148        
149        removeDefaultProperties(props);
150        File etcPropsFile = new File("/etc/build.properties");
151        if (etcPropsFile.exists()) {
152                Properties etcProps = new Properties();
153                is = new FileInputStream(etcPropsFile);
154                etcProps.load(is);
155                is.close();
156                removeDefaultProperties(etcProps);
157                props.put("/etc/build.properties", etcProps);
158        }
159        
160        response.setHeader("Content-Type", "application/json");
161        response.setStatus(HttpServletResponse.SC_OK);
162        response.getWriter().println(new JSONObject(props).toString());
163    }
164}