/* | |
* 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.tomee.website; | |
import lombok.Builder; | |
import lombok.Data; | |
import org.apache.johnzon.jaxrs.JohnzonProvider; | |
import javax.net.ssl.HttpsURLConnection; | |
import javax.ws.rs.client.ClientBuilder; | |
import javax.ws.rs.client.WebTarget; | |
import javax.ws.rs.core.MediaType; | |
import javax.ws.rs.core.Response; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.StringReader; | |
import java.io.UnsupportedEncodingException; | |
import java.security.MessageDigest; | |
import java.security.NoSuchAlgorithmException; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.TimeUnit; | |
import java.util.stream.Stream; | |
import static java.util.Collections.emptyList; | |
import static java.util.Optional.ofNullable; | |
import static java.util.stream.Collectors.toList; | |
public class Contributors { | |
private static final String GRAVATAR_BASE = "http://fr.gravatar.com/"; | |
private Contributors() { | |
// no-op | |
} | |
public static Contributor singleLoad(final WebTarget target, final String input) throws IOException { | |
try { | |
return ofNullable(loadGravatar(target, input)).orElse(loadStatic(input)); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
return loadStatic(input); | |
} | |
} | |
public static Contributor loadStatic(final String input) { | |
final String[] strings = input.split(" *\\| *"); | |
final String mail = strings[0].replaceAll("\\*$", ""); | |
final boolean committer = strings[0].endsWith("*"); | |
final String name = strings.length > 1 ? strings[1] : mail.replaceAll("@.*", ""); | |
final String picture = strings.length > 2 ? strings[2] : "../img/noimg.png"; | |
return Contributor.builder() | |
.name(name) | |
.id(mail) | |
.committer(committer) | |
.gravatar(picture) | |
.build(); | |
} | |
public static Contributor loadGravatar(final WebTarget target, final String input) throws IOException { | |
final String[] strings = input.split(" *\\| *"); | |
final boolean committer = strings[0].endsWith("*"); | |
final String mail = committer ? strings[0].substring(0, strings[0].length() - 1) : strings[0]; | |
final String hash = gravatarHash(mail); | |
final Response gravatar = target.path(hash + ".json").request(MediaType.APPLICATION_JSON_TYPE).get(); | |
if (gravatar.getStatus() != HttpsURLConnection.HTTP_OK) { | |
System.err.println("[ERROR] No gravatar for " + mail); | |
return null; | |
} | |
final Contributor contributor = ofNullable(gravatar.readEntity(Gravatar.class).getEntry()) | |
.map(e -> e[0]) | |
.map(e -> Contributor.builder() | |
.id(e.getId()) | |
.name( | |
ofNullable(e.getName()) | |
.map(n -> ofNullable(n.getFormatted()).orElse(ofNullable(n.getGivenName()).orElse("") + ofNullable(n.getFamilyName()).orElse(""))) | |
.orElseGet(() -> ofNullable(e.getDisplayName()).orElse(ofNullable(e.getPreferredUsername()).orElse(mail)))) | |
.description(e.getAboutMe()) | |
.link( | |
Stream.concat( | |
ofNullable(e.getAccounts()) | |
.map(a -> Stream.of(a).map(l -> Link.builder().name(l.getShortname()).url(l.getUrl()).build()).collect(toList())) | |
.orElse(emptyList()).stream(), | |
ofNullable(e.getUrls()) | |
.map(a -> Stream.of(a).map(l -> Link.builder().name(l.getTitle()).url(l.getValue()).build()).collect(toList())) | |
.orElse(emptyList()).stream()) | |
.collect(toList())) | |
.gravatar("http://www.gravatar.com/avatar/" + hash + "?s=140") | |
.build()) | |
.orElse(Contributor.builder().name(mail).id(mail).build()); | |
contributor.setCommitter(committer); | |
ofNullable(contributor.getLink()).ifPresent(l -> Collections.sort(l, (o1, o2) -> o1.getName().compareTo(o2.getName()))); | |
return contributor; | |
} | |
public static Collection<Contributor> load(final String contributorsList) throws IOException { // used in page.gsp | |
final WebTarget target = ClientBuilder.newClient().register(new JohnzonProvider()).target(GRAVATAR_BASE); | |
final List<Contributor> contributors = new ArrayList<>(); | |
final ExecutorService es = Executors.newFixedThreadPool(16); | |
final String rawList = contributorsList.substring(contributorsList.indexOf("<pre>") + "<pre>".length(), contributorsList.indexOf("</pre>")); | |
try (final BufferedReader reader = new BufferedReader(new StringReader(rawList))) { | |
String line; | |
while ((line = reader.readLine()) != null) { | |
line = line.trim(); | |
if (line.isEmpty() || line.startsWith("#")) { | |
continue; | |
} | |
final String mail = line; | |
es.submit(() -> { | |
Contributor contributor = null; | |
try { | |
contributor = singleLoad(target, mail); | |
} catch (final IOException e) { | |
throw new IllegalStateException(e); | |
} | |
if (contributor != null) { | |
synchronized (contributors) { | |
contributors.add(contributor); | |
} | |
} | |
}); | |
} | |
} | |
es.shutdown(); | |
try { | |
es.awaitTermination(30, TimeUnit.MINUTES); | |
} catch (final InterruptedException e) { | |
Thread.interrupted(); | |
return Collections.emptyList(); | |
} | |
Collections.sort(contributors, (o1, o2) -> o1.name.compareTo(o2.id)); | |
return contributors; | |
} | |
private static String gravatarHash(final String mail) { | |
try { | |
final MessageDigest md = MessageDigest.getInstance("MD5"); | |
byte[] cp1252s = md.digest(mail.getBytes("CP1252")); | |
final StringBuilder sb = new StringBuilder(); | |
for (final byte anArray : cp1252s) { | |
sb.append(Integer.toHexString((anArray & 0xFF) | 0x100).substring(1, 3)); | |
} | |
return sb.toString(); | |
} catch (final NoSuchAlgorithmException | UnsupportedEncodingException e) { | |
throw new IllegalStateException(e); | |
} | |
} | |
@Data | |
@Builder | |
public static class Link { | |
private String name; | |
private String url; | |
} | |
@Data | |
@Builder | |
public static class Contributor { | |
private String id; | |
private boolean committer; | |
private String name; | |
private String description; | |
private String gravatar; | |
private List<Link> link; | |
} | |
@Data | |
public static class GravatarName { | |
private String formatted; | |
private String givenName; | |
private String familyName; | |
} | |
@Data | |
public static class GravatarUrl { | |
private String value; | |
private String title; | |
} | |
@Data | |
public static class GravatarAccount { | |
private String shortname; | |
private String url; | |
} | |
@Data | |
public static class Gravatar { | |
private GravatarEntry[] entry; | |
} | |
@Data | |
public static class GravatarEntry { | |
private String id; | |
private String hash; | |
private String aboutMe; | |
private String requestHash; | |
private String profileUrl; | |
private String preferredUsername; | |
private String thumbnailUrl; | |
private GravatarName name; | |
private GravatarUrl[] urls; | |
private GravatarAccount[] accounts; | |
private String displayName; | |
} | |
} |