Java tutorial
package org.apache.maven.plugin.doap; /* * 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 java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.net.URL; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; import java.util.WeakHashMap; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.Set; import java.util.Properties; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.maven.model.Contributor; import org.apache.maven.project.MavenProject; import org.apache.maven.settings.Proxy; import org.apache.maven.settings.Settings; import org.apache.maven.wagon.proxy.ProxyInfo; import org.apache.maven.wagon.proxy.ProxyUtils; import org.codehaus.plexus.i18n.I18N; import org.codehaus.plexus.interpolation.EnvarBasedValueSource; import org.codehaus.plexus.interpolation.InterpolationException; import org.codehaus.plexus.interpolation.ObjectBasedValueSource; import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; import org.codehaus.plexus.interpolation.PropertiesBasedValueSource; import org.codehaus.plexus.interpolation.RegexBasedInterpolator; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.introspection.ClassMap; import org.codehaus.plexus.util.xml.XMLWriter; import org.codehaus.plexus.util.xml.XmlWriterUtil; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.RDFReader; import com.hp.hpl.jena.rdf.model.impl.RDFDefaultErrorHandler; /** * Utility class for {@link DoapMojo} class. * * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> * @version $Id$ * @since 1.0 */ public class DoapUtil { /** Email regex */ private static final String EMAIL_REGEX = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; /** Email pattern */ private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX); /** Magic number to repeat '=' */ private static final int REPEAT_EQUALS = 21; /** The default timeout used when fetching url, i.e. 2000. */ public static final int DEFAULT_TIMEOUT = 2000; /** RDF resource attribute */ protected static final String RDF_RESOURCE = "rdf:resource"; /** RDF nodeID attribute */ protected static final String RDF_NODE_ID = "rdf:nodeID"; /** DoaP Organizations stored by name */ private static Map<String, DoapUtil.Organization> organizations = new HashMap<String, DoapUtil.Organization>(); /** * Write comments in the DOAP file header * * @param writer not null */ public static void writeHeader(XMLWriter writer) { XmlWriterUtil.writeLineBreak(writer); XmlWriterUtil.writeCommentLineBreak(writer); XmlWriterUtil.writeComment(writer, StringUtils.repeat("=", REPEAT_EQUALS) + " - DO NOT EDIT THIS FILE! - " + StringUtils.repeat("=", REPEAT_EQUALS)); XmlWriterUtil.writeCommentLineBreak(writer); XmlWriterUtil.writeComment(writer, " "); XmlWriterUtil.writeComment(writer, "Any modifications will be overwritten."); XmlWriterUtil.writeComment(writer, " "); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US); XmlWriterUtil.writeComment(writer, "Generated by Maven Doap Plugin " + getPluginVersion() + " on " + dateFormat.format(new Date(System.currentTimeMillis()))); XmlWriterUtil.writeComment(writer, "See: http://maven.apache.org/plugins/maven-doap-plugin/"); XmlWriterUtil.writeComment(writer, " "); XmlWriterUtil.writeCommentLineBreak(writer); XmlWriterUtil.writeLineBreak(writer); } /** * Write comment. * * @param writer not null * @param comment not null * @throws IllegalArgumentException if comment is null or empty * @since 1.1 */ public static void writeComment(XMLWriter writer, String comment) throws IllegalArgumentException { if (StringUtils.isEmpty(comment)) { throw new IllegalArgumentException("comment should be defined"); } XmlWriterUtil.writeLineBreak(writer); XmlWriterUtil.writeCommentText(writer, comment, 2); } /** * @param writer not null * @param xmlnsPrefix could be null * @param name not null * @param value could be null. In this case, the element is not written. * @throws IllegalArgumentException if name is null or empty */ public static void writeElement(XMLWriter writer, String xmlnsPrefix, String name, String value) throws IllegalArgumentException { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("name should be defined"); } if (value != null) { writeStartElement(writer, xmlnsPrefix, name); writer.writeText(value); writer.endElement(); } } /** * @param writer not null * @param xmlnsPrefix could be null * @param name not null * @param lang not null * @param value could be null. In this case, the element is not written. * @throws IllegalArgumentException if name is null or empty */ public static void writeElement(XMLWriter writer, String xmlnsPrefix, String name, String value, String lang) throws IllegalArgumentException { if (StringUtils.isEmpty(lang)) { writeElement(writer, xmlnsPrefix, name, value); return; } if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("name should be defined"); } if (value != null) { writeStartElement(writer, xmlnsPrefix, name); writer.addAttribute("xml:lang", lang); writer.writeText(value); writer.endElement(); } } /** * @param writer not null * @param xmlnsPrefix could be null * @param name not null * @throws IllegalArgumentException if name is null or empty * @since 1.1 */ public static void writeStartElement(XMLWriter writer, String xmlnsPrefix, String name) throws IllegalArgumentException { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("name should be defined"); } if (StringUtils.isNotEmpty(xmlnsPrefix)) { writer.startElement(xmlnsPrefix + ":" + name); } else { writer.startElement(name); } } /** * @param writer not null * @param xmlnsPrefix could be null * @param name not null * @param value could be null. In this case, the element is not written. * @throws IllegalArgumentException if name is null or empty */ public static void writeRdfResourceElement(XMLWriter writer, String xmlnsPrefix, String name, String value) throws IllegalArgumentException { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("name should be defined"); } if (value != null) { writeStartElement(writer, xmlnsPrefix, name); writer.addAttribute(RDF_RESOURCE, value); writer.endElement(); } } /** * @param writer not null * @param name not null * @param value could be null. In this case, the element is not written. * @throws IllegalArgumentException if name is null or empty */ public static void writeRdfNodeIdElement(XMLWriter writer, String xmlnsPrefix, String name, String value) throws IllegalArgumentException { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("name should be defined"); } if (value != null) { writeStartElement(writer, xmlnsPrefix, name); writer.addAttribute(RDF_NODE_ID, value); writer.endElement(); } } /** * @param i18n the internationalization component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null list of <code>{@link Contributor}</code> which have a <code>developer</code> DOAP role. */ public static List<Contributor> getContributorsWithDeveloperRole(I18N i18n, List<Contributor> developersOrContributors) { return filterContributorsByDoapRoles(i18n, developersOrContributors).get("developers"); } /** * @param i18n the internationalization component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null list of <code>{@link Contributor}</code> which have a <code>documenter</code> DOAP role. */ public static List<Contributor> getContributorsWithDocumenterRole(I18N i18n, List<Contributor> developersOrContributors) { return filterContributorsByDoapRoles(i18n, developersOrContributors).get("documenters"); } /** * @param i18n the internationalization component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null list of <code>{@link Contributor}</code> which have an <code>helper</code> DOAP role. */ public static List<Contributor> getContributorsWithHelperRole(I18N i18n, List<Contributor> developersOrContributors) { return filterContributorsByDoapRoles(i18n, developersOrContributors).get("helpers"); } /** * @param i18n the internationalization component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null list of <code>{@link Contributor}</code> which have a <code>maintainer</code> DOAP role. */ public static List<Contributor> getContributorsWithMaintainerRole(I18N i18n, List<Contributor> developersOrContributors) { return filterContributorsByDoapRoles(i18n, developersOrContributors).get("maintainers"); } /** * @param i18n the internationalization component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null list of <code>{@link Contributor}</code> which have a <code>tester</code> DOAP role. */ public static List<Contributor> getContributorsWithTesterRole(I18N i18n, List<Contributor> developersOrContributors) { return filterContributorsByDoapRoles(i18n, developersOrContributors).get("testers"); } /** * @param i18n the internationalization component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null list of <code>{@link Contributor}</code> which have a <code>translator</code> DOAP role. */ public static List<Contributor> getContributorsWithTranslatorRole(I18N i18n, List<Contributor> developersOrContributors) { return filterContributorsByDoapRoles(i18n, developersOrContributors).get("translators"); } /** * @param i18n the internationalization component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null list of <code>{@link Contributor}</code> which have an <code>unknown</code> DOAP role. */ public static List<Contributor> getContributorsWithUnknownRole(I18N i18n, List<Contributor> developersOrContributors) { return filterContributorsByDoapRoles(i18n, developersOrContributors).get("unknowns"); } /** * Utility class for keeping track of DOAP organizations in the DoaP mojo. * * @author <a href="mailto:t.fliss@gmail.com">Tim Fliss</a> * @version $Id$ * @since 1.1 */ public static class Organization { private String name; private String url; private List<String> members = new LinkedList<String>(); public Organization(String name, String url) { this.name = name; this.url = url; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setUrl(String url) { this.url = url; } public String getUrl() { return url; } public void addMember(String nodeId) { members.add(nodeId); } public List<String> getMembers() { return members; } } /** * put an organization from the pom file in the organization list. * * @param name from the pom file (e.g. Yoyodyne) * @param url from the pom file (e.g. http://yoyodyne.example.org/about) * @return the existing organization if a duplicate, or a new one. */ public static DoapUtil.Organization addOrganization(String name, String url) { Organization organization = organizations.get(name); if (organization == null) { organization = new DoapUtil.Organization(name, url); } organizations.put(name, organization); return organization; } // unique RDF blank node index scoped internal to the DOAP file private static int nodeNumber = 1; /** * get a unique (within the DoaP file) RDF blank node ID * * @return the nodeID * @see <a href="http://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-blank-nodes"> * http://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-blank-nodes</a> */ public static String getNodeId() { return "b" + nodeNumber++; } /** * get the set of Organizations that people are members of * * @return Map.EntrySet of DoapUtil.Organization */ public static Set<Entry<String, DoapUtil.Organization>> getOrganizations() { return organizations.entrySet(); } /** * Validate the given DOAP file. * * @param doapFile not null and should exists. * @return an empty list if the DOAP file is valid, otherwise a list of errors. * @since 1.1 */ public static List<String> validate(File doapFile) { if (doapFile == null || !doapFile.isFile()) { throw new IllegalArgumentException("The DOAP file should exist"); } Model model = ModelFactory.createDefaultModel(); RDFReader r = model.getReader("RDF/XML"); r.setProperty("error-mode", "strict-error"); final List<String> errors = new ArrayList<String>(); r.setErrorHandler(new RDFDefaultErrorHandler() { @Override public void error(Exception e) { errors.add(e.getMessage()); } }); try { r.read(model, doapFile.toURI().toURL().toString()); } catch (MalformedURLException e) { // ignored } return errors; } /** * @param str not null * @return <code>true</code> if the str parameter is a valid email, <code>false</code> otherwise. * @since 1.1 */ public static boolean isValidEmail(String str) { if (StringUtils.isEmpty(str)) { return false; } Matcher matcher = EMAIL_PATTERN.matcher(str); return matcher.matches(); } /** * Fetch an URL * * @param settings the user settings used to fetch the url with an active proxy, if defined. * @param url the url to fetch * @throws IOException if any * @see #DEFAULT_TIMEOUT * @since 1.1 */ public static void fetchURL(Settings settings, URL url) throws IOException { if (url == null) { throw new IllegalArgumentException("The url is null"); } if ("file".equals(url.getProtocol())) { InputStream in = null; try { in = url.openStream(); } finally { IOUtil.close(in); } return; } // http, https... HttpClient httpClient = new HttpClient(new MultiThreadedHttpConnectionManager()); httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(DEFAULT_TIMEOUT); httpClient.getHttpConnectionManager().getParams().setSoTimeout(DEFAULT_TIMEOUT); httpClient.getParams().setBooleanParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true); // Some web servers don't allow the default user-agent sent by httpClient httpClient.getParams().setParameter(HttpMethodParams.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"); if (settings != null && settings.getActiveProxy() != null) { Proxy activeProxy = settings.getActiveProxy(); ProxyInfo proxyInfo = new ProxyInfo(); proxyInfo.setNonProxyHosts(activeProxy.getNonProxyHosts()); if (StringUtils.isNotEmpty(activeProxy.getHost()) && !ProxyUtils.validateNonProxyHosts(proxyInfo, url.getHost())) { httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), activeProxy.getPort()); if (StringUtils.isNotEmpty(activeProxy.getUsername()) && activeProxy.getPassword() != null) { Credentials credentials = new UsernamePasswordCredentials(activeProxy.getUsername(), activeProxy.getPassword()); httpClient.getState().setProxyCredentials(AuthScope.ANY, credentials); } } } GetMethod getMethod = new GetMethod(url.toString()); try { int status; try { status = httpClient.executeMethod(getMethod); } catch (SocketTimeoutException e) { // could be a sporadic failure, one more retry before we give up status = httpClient.executeMethod(getMethod); } if (status != HttpStatus.SC_OK) { throw new FileNotFoundException(url.toString()); } } finally { getMethod.releaseConnection(); } } /** * Interpolate a string with project and settings. * * @param value could be null * @param project not null * @param settings could be null * @return the value trimmed and interpolated or null if the interpolation doesn't work. * @since 1.1 */ public static String interpolate(String value, final MavenProject project, Settings settings) { if (project == null) { throw new IllegalArgumentException("project is required"); } if (value == null) { return value; } if (!value.contains("${")) { return value.trim(); } RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); try { interpolator.addValueSource(new EnvarBasedValueSource()); } catch (IOException e) { } interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties())); interpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties())); interpolator.addValueSource(new PrefixedObjectValueSource("project", project)); interpolator.addValueSource(new PrefixedObjectValueSource("pom", project)); interpolator.addValueSource(new ObjectBasedValueSource(project) { @Override public Object getValue(String expression) { try { return ReflectionValueExtractor.evaluate(expression, project, true); } catch (Exception e) { addFeedback("Failed to extract \'" + expression + "\' from: " + project, e); } return null; } }); if (settings != null) { interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings)); } String interpolatedValue = value; try { interpolatedValue = interpolator.interpolate(value).trim(); } catch (InterpolationException e) { } if (interpolatedValue.startsWith("${")) { return null; } return interpolatedValue; } // ---------------------------------------------------------------------- // Private methods // ---------------------------------------------------------------------- /** * Filter the developers/contributors roles by the keys from {@link I18N#getBundle()}. <br/> * I18N roles supported in DOAP, i.e. <code>maintainer</code>, <code>developer</code>, <code>documenter</code>, * <code>translator</code>, <code>tester</code>, <code>helper</code>. <br/> * <b>Note:</b> Actually, only English keys are used. * * @param i18n i18n component * @param developersOrContributors list of <code>{@link Contributor}</code> * @return a none null map with <code>maintainers</code>, <code>developers</code>, <code>documenters</code>, * <code>translators</code>, <code>testers</code>, <code>helpers</code>, <code>unknowns</code> as keys and * list of <code>{@link Contributor}</code> as value. */ private static Map<String, List<Contributor>> filterContributorsByDoapRoles(I18N i18n, List<Contributor> developersOrContributors) { Map<String, List<Contributor>> returnMap = new HashMap<String, List<Contributor>>(7); returnMap.put("maintainers", new ArrayList<Contributor>()); returnMap.put("developers", new ArrayList<Contributor>()); returnMap.put("documenters", new ArrayList<Contributor>()); returnMap.put("translators", new ArrayList<Contributor>()); returnMap.put("testers", new ArrayList<Contributor>()); returnMap.put("helpers", new ArrayList<Contributor>()); returnMap.put("unknowns", new ArrayList<Contributor>()); if (developersOrContributors == null || developersOrContributors.isEmpty()) { return returnMap; } for (Contributor contributor : developersOrContributors) { List<String> roles = contributor.getRoles(); if (roles != null && roles.size() != 0) { for (String role : roles) { role = role.toLowerCase(Locale.ENGLISH); if (role.contains(getLowerCaseString(i18n, "doap.maintainer"))) { if (!returnMap.get("maintainers").contains(contributor)) { returnMap.get("maintainers").add(contributor); } } else if (role.contains(getLowerCaseString(i18n, "doap.developer"))) { if (!returnMap.get("developers").contains(contributor)) { returnMap.get("developers").add(contributor); } } else if (role.contains(getLowerCaseString(i18n, "doap.documenter"))) { if (!returnMap.get("documenters").contains(contributor)) { returnMap.get("documenters").add(contributor); } } else if (role.contains(getLowerCaseString(i18n, "doap.translator"))) { if (!returnMap.get("translators").contains(contributor)) { returnMap.get("translators").add(contributor); } } else if (role.contains(getLowerCaseString(i18n, "doap.tester"))) { if (!returnMap.get("testers").contains(contributor)) { returnMap.get("testers").add(contributor); } } else if (role.contains(getLowerCaseString(i18n, "doap.helper"))) { if (!returnMap.get("helpers").contains(contributor)) { returnMap.get("helpers").add(contributor); } } else if (role.contains(getLowerCaseString(i18n, "doap.emeritus"))) { // Don't add as developer nor as contributor as the person is no longer involved } else { if (!returnMap.get("unknowns").contains(contributor)) { returnMap.get("unknowns").add(contributor); } } } } else { if (!returnMap.get("unknowns").contains(contributor)) { returnMap.get("unknowns").add(contributor); } } } return returnMap; } /** * @param i18n not null * @param key not null * @return lower case value for the key in the i18n bundle. */ private static String getLowerCaseString(I18N i18n, String key) { return i18n.getString("doap-person", Locale.ENGLISH, key).toLowerCase(Locale.ENGLISH); } /** * @return the Maven artefact version. */ private static String getPluginVersion() { Properties pomProperties = new Properties(); InputStream is = null; try { is = DoapUtil.class.getResourceAsStream( "/META-INF/maven/org.apache.maven.plugins/maven-doap-plugin/pom.properties"); if (is == null) { return "<unknown>"; } pomProperties.load(is); return pomProperties.getProperty("version", "<unknown>"); } catch (IOException e) { return "<unknown>"; } finally { IOUtil.close(is); } } /** * Fork of {@link org.codehaus.plexus.interpolation.reflection.ReflectionValueExtractor} to care of list or arrays. */ static class ReflectionValueExtractor { @SuppressWarnings("rawtypes") private static final Class[] CLASS_ARGS = new Class[0]; private static final Object[] OBJECT_ARGS = new Object[0]; /** * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen * space overflows due to retention of discarded classloaders. */ @SuppressWarnings("rawtypes") private static final Map<Class, ClassMap> classMaps = new WeakHashMap<Class, ClassMap>(); private ReflectionValueExtractor() { } public static Object evaluate(String expression, Object root) throws Exception { return evaluate(expression, root, true); } // TODO: don't throw Exception public static Object evaluate(String expression, Object root, boolean trimRootToken) throws Exception { // if the root token refers to the supplied root object parameter, remove it. if (trimRootToken) { expression = expression.substring(expression.indexOf('.') + 1); } Object value = root; // ---------------------------------------------------------------------- // Walk the dots and retrieve the ultimate value desired from the // MavenProject instance. // ---------------------------------------------------------------------- StringTokenizer parser = new StringTokenizer(expression, "."); while (parser.hasMoreTokens()) { String token = parser.nextToken(); if (value == null) { return null; } StringTokenizer parser2 = new StringTokenizer(token, "[]"); int index = -1; if (parser2.countTokens() > 1) { token = parser2.nextToken(); try { index = Integer.valueOf(parser2.nextToken()).intValue(); } catch (NumberFormatException e) { } } final ClassMap classMap = getClassMap(value.getClass()); final String methodBase = StringUtils.capitalizeFirstLetter(token); String methodName = "get" + methodBase; Method method = classMap.findMethod(methodName, CLASS_ARGS); if (method == null) { // perhaps this is a boolean property?? methodName = "is" + methodBase; method = classMap.findMethod(methodName, CLASS_ARGS); } if (method == null) { return null; } value = method.invoke(value, OBJECT_ARGS); if (value == null) { return null; } if (Collection.class.isAssignableFrom(value.getClass())) { ClassMap classMap2 = getClassMap(value.getClass()); Method method2 = classMap2.findMethod("toArray", CLASS_ARGS); value = method2.invoke(value, OBJECT_ARGS); } if (value.getClass().isArray()) { value = ((Object[]) value)[index]; } } return value; } private static ClassMap getClassMap(Class<? extends Object> clazz) { ClassMap classMap = classMaps.get(clazz); if (classMap == null) { classMap = new ClassMap(clazz); classMaps.put(clazz, classMap); } return classMap; } } }