Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. 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. For additional information regarding * copyright in this work, please see the NOTICE file in the top level * directory of this distribution. * * Source file modified from the original ASF source; all changes made * are also under Apache License. */ package org.tightblog.service; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.tightblog.domain.AtomEnclosure; import org.tightblog.domain.CommentSearchCriteria; import org.tightblog.domain.Weblog; import org.tightblog.domain.WeblogCategory; import org.tightblog.domain.WeblogEntry; import org.tightblog.domain.WeblogEntryComment; import org.tightblog.domain.WeblogEntryComment.ValidationResult; import org.tightblog.domain.WeblogEntrySearchCriteria; import org.tightblog.domain.WebloggerProperties; import org.tightblog.repository.WeblogEntryCommentRepository; import org.tightblog.repository.WeblogEntryRepository; import org.tightblog.repository.WebloggerPropertiesRepository; import org.tightblog.util.Utilities; import org.commonmark.renderer.html.HtmlRenderer; import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.jsoup.Jsoup; import org.jsoup.safety.Whitelist; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.stream.Collectors; /** * Weblog entry and comment management. */ @Component public class WeblogEntryManager { private static Logger log = LoggerFactory.getLogger(WeblogEntryManager.class); private WeblogManager weblogManager; private WeblogEntryRepository weblogEntryRepository; private WeblogEntryCommentRepository weblogEntryCommentRepository; private WebloggerPropertiesRepository webloggerPropertiesRepository; private URLService urlService; private String akismetApiKey; private LuceneIndexer luceneIndexer; @PersistenceContext private EntityManager entityManager; @Autowired public WeblogEntryManager(WeblogManager weblogManager, WeblogEntryRepository weblogEntryRepository, WeblogEntryCommentRepository weblogEntryCommentRepository, URLService urlService, @Lazy LuceneIndexer luceneIndexer, WebloggerPropertiesRepository webloggerPropertiesRepository, @Value("${akismet.apiKey:#{null}}") String akismetApiKey) { this.luceneIndexer = luceneIndexer; this.weblogManager = weblogManager; this.weblogEntryRepository = weblogEntryRepository; this.weblogEntryCommentRepository = weblogEntryCommentRepository; this.webloggerPropertiesRepository = webloggerPropertiesRepository; this.urlService = urlService; this.akismetApiKey = akismetApiKey; } /** * Save comment. * * @param refreshWeblog true if weblog should be marked for cache update, i.e., likely * rendering change to accommodate new or removed comment, vs. one * still requiring moderation. */ public void saveComment(WeblogEntryComment comment, boolean refreshWeblog) { comment.setWeblog(comment.getWeblogEntry().getWeblog()); weblogEntryCommentRepository.saveAndFlush(comment); weblogEntryCommentRepository.evictWeblogCommentCounts(comment.getWeblog()); if (refreshWeblog) { weblogEntryCommentRepository.evictWeblogEntryCommentCounts(comment.getWeblogEntry()); weblogManager.saveWeblog(comment.getWeblog(), true); } } /** * Remove comment and invalidate its parent weblog's cache */ public void removeComment(WeblogEntryComment comment) { weblogEntryCommentRepository.deleteById(comment.getId()); boolean externallyViewable = WeblogEntryComment.ApprovalStatus.APPROVED.equals(comment.getStatus()); weblogManager.saveWeblog(comment.getWeblogEntry().getWeblog(), externallyViewable); weblogEntryCommentRepository.evictWeblogCommentCounts(comment.getWeblog()); if (externallyViewable) { weblogEntryCommentRepository.evictWeblogEntryCommentCounts(comment.getWeblogEntry()); } } /** * Recategorize all entries with one category to another. */ public void moveWeblogCategoryContents(WeblogCategory srcCat, WeblogCategory destCat) { // get all entries in category and subcats WeblogEntrySearchCriteria wesc = new WeblogEntrySearchCriteria(); wesc.setWeblog(srcCat.getWeblog()); wesc.setCategoryName(srcCat.getName()); List<WeblogEntry> results = getWeblogEntries(wesc); // Loop through entries in src cat, assign them to dest cat for (WeblogEntry entry : results) { entry.setCategory(destCat); weblogEntryRepository.saveAndFlush(entry); } } /** * Check for any scheduled weblog entries whose publication time has been * reached and promote them. */ public void promoteScheduledEntries() { log.debug("promoting scheduled entries..."); try { Instant now = Instant.now(); log.debug("looking up scheduled entries older than {}", now); // get all published entries older than current time WeblogEntrySearchCriteria wesc = new WeblogEntrySearchCriteria(); wesc.setEndDate(now); wesc.setStatus(WeblogEntry.PubStatus.SCHEDULED); List<WeblogEntry> scheduledEntries = getWeblogEntries(wesc); log.debug("promoting {} entries to PUBLISHED state", scheduledEntries.size()); for (WeblogEntry entry : scheduledEntries) { entry.setStatus(WeblogEntry.PubStatus.PUBLISHED); saveWeblogEntry(entry); } // take a second pass to trigger reindexing // this is because we need the updated entries flushed first for (WeblogEntry entry : scheduledEntries) { // trigger search index on entry luceneIndexer.updateIndex(entry, false); } } catch (Exception e) { log.error("Unexpected exception running task", e); } log.debug("finished promoting entries"); } public void saveWeblogEntry(WeblogEntry entry) { if (entry.getCategory() == null) { // Entry is invalid without category, so use first one found if not provided WeblogCategory cat = entry.getWeblog().getWeblogCategories().iterator().next(); entry.setCategory(cat); } if (entry.getAnchor() == null || entry.getAnchor().trim().equals("")) { entry.setAnchor(this.createAnchor(entry)); } // if the entry was published to future, set status as SCHEDULED // we only consider an entry future published if it is scheduled // more than 1 minute into the future if (WeblogEntry.PubStatus.PUBLISHED.equals(entry.getStatus()) && entry.getPubTime().isAfter(Instant.now().plus(1, ChronoUnit.MINUTES))) { entry.setStatus(WeblogEntry.PubStatus.SCHEDULED); } // Store value object (creates new or updates existing) Instant now = Instant.now(); entry.setUpdateTime(now); weblogEntryRepository.save(entry); weblogManager.saveWeblog(entry.getWeblog(), true); } public void removeWeblogEntry(WeblogEntry entry) { weblogEntryCommentRepository.deleteByWeblogEntry(entry); weblogEntryRepository.delete(entry); weblogManager.saveWeblog(entry.getWeblog(), true); } /** * Find nearest published blog entry before or after a given target date. Useful for date-based * pagination where it is desired to determine the next or previous time period that * contains a blog entry. * * @param weblog weblog whose entries to search * @param categoryName Category name that entry must belong to or null if entry may belong to any category * @param targetDate Earliest (if succeeding = true) or latest (succeeding = false) publish time of blog entry * @param succeeding If true, find the first blog entry whose publish time comes after the targetDate, if false, the * entry closest to but before the targetDate. * @return WeblogEntry meeting the above criteria or null if no entry matches */ public WeblogEntry findNearestWeblogEntry(Weblog weblog, String categoryName, LocalDateTime targetDate, boolean succeeding) { WeblogEntry nearestEntry = null; WeblogEntrySearchCriteria wesc = new WeblogEntrySearchCriteria(); wesc.setWeblog(weblog); wesc.setCategoryName(categoryName); wesc.setStatus(WeblogEntry.PubStatus.PUBLISHED); wesc.setMaxResults(1); if (succeeding) { wesc.setStartDate(targetDate.atZone(ZoneId.systemDefault()).toInstant()); wesc.setSortOrder(WeblogEntrySearchCriteria.SortOrder.ASCENDING); } else { wesc.setEndDate(targetDate.atZone(ZoneId.systemDefault()).toInstant()); wesc.setSortOrder(WeblogEntrySearchCriteria.SortOrder.DESCENDING); } List entries = getWeblogEntries(wesc); if (entries.size() > 0) { nearestEntry = (WeblogEntry) entries.get(0); } return nearestEntry; } /** * Get the WeblogEntry following, chronologically, the current entry. * * @param current The "current" WeblogEntry */ public WeblogEntry getNextPublishedEntry(WeblogEntry current) { WeblogEntry entry = null; List<WeblogEntry> entryList = getNextPrevEntries(current, true); if (entryList != null && entryList.size() > 0) { entry = entryList.get(0); } return entry; } /** * Get the WeblogEntry prior to, chronologically, the current entry. * * @param current The "current" WeblogEntry */ public WeblogEntry getPreviousPublishedEntry(WeblogEntry current) { WeblogEntry entry = null; List<WeblogEntry> entryList = getNextPrevEntries(current, false); if (entryList != null && entryList.size() > 0) { entry = entryList.get(0); } return entry; } private List<WeblogEntry> getNextPrevEntries(WeblogEntry current, boolean next) { if (current == null || current.getPubTime() == null) { return Collections.emptyList(); } TypedQuery<WeblogEntry> query; List<Object> params = new ArrayList<>(); int size = 0; String queryString = "SELECT e FROM WeblogEntry e WHERE "; StringBuilder whereClause = new StringBuilder(); params.add(size++, current.getWeblog()); whereClause.append("e.weblog = ?").append(size); params.add(size++, current.getId()); whereClause.append(" AND e.id <> ?").append(size); params.add(size++, WeblogEntry.PubStatus.PUBLISHED); whereClause.append(" AND e.status = ?").append(size); params.add(size++, current.getPubTime()); if (next) { whereClause.append(" AND e.pubTime >= ?").append(size); whereClause.append(" ORDER BY e.pubTime ASC, e.id ASC"); } else { whereClause.append(" AND e.pubTime <= ?").append(size); whereClause.append(" ORDER BY e.pubTime DESC, e.id DESC"); } query = entityManager.createQuery(queryString + whereClause.toString(), WeblogEntry.class); for (int i = 0; i < params.size(); i++) { query.setParameter(i + 1, params.get(i)); } query.setMaxResults(1); return query.getResultList(); } private QueryData createEntryQueryString(WeblogEntrySearchCriteria criteria) { QueryData qd = new QueryData(); int size = 0; qd.queryString = "SELECT e FROM WeblogEntry e"; if (criteria.getTag() == null) { qd.queryString += " WHERE 1=1 "; } else { // subquery to avoid this problem with Derby: http://stackoverflow.com/a/480536 qd.queryString += " WHERE EXISTS ( Select 1 from WeblogEntryTag t " + "where t.weblogEntry.id = e.id AND ("; qd.params.add(size++, criteria.getTag()); qd.queryString += " t.name = ?" + size; qd.queryString += ")) "; } if (criteria.getWeblog() != null) { qd.params.add(size++, criteria.getWeblog().getId()); qd.queryString += "AND e.weblog.id = ?" + size; } qd.params.add(size++, Boolean.TRUE); qd.queryString += " AND e.weblog.visible = ?" + size; if (criteria.getUser() != null) { qd.params.add(size++, criteria.getUser().getUserName()); qd.queryString += " AND e.creatorUserName = ?" + size; } if (criteria.getStartDate() != null) { qd.params.add(size++, criteria.getStartDate()); qd.queryString += " AND e.pubTime >= ?" + size; } if (criteria.getEndDate() != null) { qd.params.add(size++, criteria.getEndDate()); qd.queryString += " AND e.pubTime <= ?" + size; } if (!StringUtils.isEmpty(criteria.getCategoryName())) { qd.params.add(size++, criteria.getCategoryName()); qd.queryString += " AND e.category.name = ?" + size; } if (criteria.getStatus() != null) { qd.params.add(size++, criteria.getStatus()); qd.queryString += " AND e.status = ?" + size; } if (StringUtils.isNotEmpty(criteria.getText())) { qd.params.add(size++, '%' + criteria.getText() + '%'); qd.queryString += " AND ( e.text LIKE ?" + size; qd.queryString += " OR e.summary LIKE ?" + size; qd.queryString += " OR e.title LIKE ?" + size; qd.queryString += ") "; } qd.queryString += " ORDER BY "; qd.queryString += WeblogEntrySearchCriteria.SortBy.UPDATE_TIME.equals(criteria.getSortBy()) ? " e.updateTime " : " e.pubTime "; String sortOrder = WeblogEntrySearchCriteria.SortOrder.ASCENDING.equals(criteria.getSortOrder()) ? " ASC " : " DESC "; qd.queryString += sortOrder + ", e.id " + sortOrder; return qd; } /** * Get WeblogEntries by offset/length as list in reverse chronological order. * The range offset and list arguments enable paging through query results. * * @param criteria WeblogEntrySearchCriteria object listing desired search parameters * @return List of WeblogEntry objects in order specified by search criteria */ public List<WeblogEntry> getWeblogEntries(WeblogEntrySearchCriteria criteria) { QueryData qd = createEntryQueryString(criteria); TypedQuery<WeblogEntry> query = entityManager.createQuery(qd.queryString, WeblogEntry.class); for (int i = 0; i < qd.params.size(); i++) { query.setParameter(i + 1, qd.params.get(i)); } if (criteria.getOffset() != 0) { query.setFirstResult(criteria.getOffset()); } if (criteria.getMaxResults() != -1) { query.setMaxResults(criteria.getMaxResults()); } List<WeblogEntry> results = query.getResultList(); if (criteria.isCalculatePermalinks()) { results = results.stream().peek(we -> we.setPermalink(urlService.getWeblogEntryURL(we))) .collect(Collectors.toList()); } return results; } public WeblogEntry getWeblogEntryByAnchor(Weblog weblog, String anchor) { WeblogEntry entry = weblogEntryRepository.findByWeblogAndAnchor(weblog, anchor); if (entry != null) { entry.setCommentRepository(weblogEntryCommentRepository); } return entry; } /** * Create unique anchor for weblog entry. */ public String createAnchor(WeblogEntry entry) { // Check for uniqueness of anchor String base = createAnchorBase(entry); String name = base; int count = 0; while (true) { if (count++ > 0) { name = base + count; } WeblogEntry entryTest = weblogEntryRepository.findByWeblogAndAnchor(entry.getWeblog(), name); if (entryTest == null) { break; } } return name; } /** * Create anchor for weblog entry, based on title or text */ private String createAnchorBase(WeblogEntry entry) { // Use title (minus non-alphanumeric characters) String base; if (!StringUtils.isEmpty(entry.getTitle())) { base = Utilities.replaceNonAlphanumeric(entry.getTitle(), ' ').trim(); } else { // try text base = Utilities.replaceNonAlphanumeric(entry.getText(), ' ').trim(); } // Use only the first 4 words StringTokenizer toker = new StringTokenizer(base); String tmp = null; int count = 0; while (toker.hasMoreTokens() && count < 5) { String s = toker.nextToken(); s = s.toLowerCase(); tmp = (tmp == null) ? s : tmp + "-" + s; count++; } base = tmp; return base; } private QueryData createCommentQueryString(CommentSearchCriteria csc) { QueryData cqd = new QueryData(); int size = 0; cqd.queryString = "SELECT c FROM WeblogEntryComment c"; StringBuilder whereClause = new StringBuilder(); if (csc.getEntry() != null) { cqd.params.add(size++, csc.getEntry()); appendConjuctionToWhereClause(whereClause, "c.weblogEntry = ?").append(size); } else { if (csc.getWeblog() != null) { cqd.params.add(size++, csc.getWeblog()); appendConjuctionToWhereClause(whereClause, "c.weblog = ?").append(size); } if (csc.getCategoryName() != null) { cqd.params.add(size++, csc.getCategoryName()); appendConjuctionToWhereClause(whereClause, "c.weblogEntry.category.name = ?").append(size); } } if (csc.getSearchText() != null) { cqd.params.add(size++, "%" + csc.getSearchText().toUpperCase() + "%"); appendConjuctionToWhereClause(whereClause, "upper(c.content) LIKE ?").append(size); } if (csc.getStartDate() != null) { cqd.params.add(size++, csc.getStartDate()); appendConjuctionToWhereClause(whereClause, "c.postTime >= ?").append(size); } if (csc.getEndDate() != null) { cqd.params.add(size++, csc.getEndDate()); appendConjuctionToWhereClause(whereClause, "c.postTime <= ?").append(size); } if (csc.getStatus() != null) { cqd.params.add(size++, csc.getStatus()); appendConjuctionToWhereClause(whereClause, "c.status = ?").append(size); } if (whereClause.length() != 0) { cqd.queryString += " WHERE " + whereClause.toString(); } if (csc.isReverseChrono()) { cqd.queryString += " ORDER BY c.postTime DESC"; } else { cqd.queryString += " ORDER BY c.postTime ASC"; } return cqd; } private static class QueryData { String queryString; List<Object> params = new ArrayList<>(); } /** * Get Weblog Entries grouped by calendar day. * * @param wesc WeblogEntrySearchCriteria object listing desired search parameters * @return Map of Lists of WeblogEntries keyed by calendar day */ public Map<LocalDate, List<WeblogEntry>> getDateToWeblogEntryMap(WeblogEntrySearchCriteria wesc) { Map<LocalDate, List<WeblogEntry>> map = new TreeMap<>(Collections.reverseOrder()); List<WeblogEntry> entries = getWeblogEntries(wesc); for (WeblogEntry entry : entries) { entry.setCommentRepository(weblogEntryCommentRepository); LocalDate tmp = entry.getPubTime() == null ? LocalDate.now() : entry.getPubTime().atZone(ZoneId.systemDefault()).toLocalDate(); List<WeblogEntry> dayEntries = map.computeIfAbsent(tmp, k -> new ArrayList<>()); dayEntries.add(entry); } return map; } /** * Generic comments query method. * * @param csc CommentSearchCriteria object with fields indicating search criteria * @return list of comments fitting search criteria */ public List<WeblogEntryComment> getComments(CommentSearchCriteria csc) { QueryData cqd = createCommentQueryString(csc); TypedQuery<WeblogEntryComment> query = entityManager.createQuery(cqd.queryString, WeblogEntryComment.class); if (csc.getOffset() != 0) { query.setFirstResult(csc.getOffset()); } if (csc.getMaxResults() != -1) { query.setMaxResults(csc.getMaxResults()); } for (int i = 0; i < cqd.params.size(); i++) { query.setParameter(i + 1, cqd.params.get(i)); } return query.getResultList(); } /** * Determine whether further comments for a particular blog entry are allowed. * @return true if additional comments may be made, false otherwise. */ public boolean canSubmitNewComments(WeblogEntry entry) { if (!entry.isPublished()) { return false; } if (WebloggerProperties.CommentPolicy.NONE .equals(webloggerPropertiesRepository.findOrNull().getCommentPolicy())) { return false; } if (WebloggerProperties.CommentPolicy.NONE.equals(entry.getWeblog().getAllowComments())) { return false; } if (entry.getCommentDays() == 0) { return false; } if (entry.getCommentDays() < 0) { return true; } boolean ret = false; Instant inPubTime = entry.getPubTime(); if (inPubTime != null) { Instant lastCommentDay = inPubTime.plus(entry.getCommentDays(), ChronoUnit.DAYS); if (Instant.now().isBefore(lastCommentDay)) { ret = true; } } return ret; } /** * Appends given expression to given whereClause. If whereClause already * has other conditions, an " AND " is also appended before appending * the expression * * @param whereClause The given where Clauuse * @param expression The given expression * @return the whereClause. */ private static StringBuilder appendConjuctionToWhereClause(StringBuilder whereClause, String expression) { if (whereClause.length() != 0 && expression.length() != 0) { whereClause.append(" AND "); } return whereClause.append(expression); } /** * Process the blog text based on whether Commonmark and/or JSoup tag * filtering is activated. This method must *NOT* alter the contents of * the original entry object, to allow the blogger to return to his original * text for additional editing as desired. * * @param format Weblog.EditFormat indicating input format of string * @param str String to which to apply processing. * @return the transformed text */ public String processBlogText(Weblog.EditFormat format, String str) { String ret = str; if (Weblog.EditFormat.COMMONMARK.equals(format) && ret != null) { Parser parser = Parser.builder().build(); Node document = parser.parse(ret); HtmlRenderer renderer = HtmlRenderer.builder().build(); ret = renderer.render(document); } if (ret != null) { WebloggerProperties props = webloggerPropertiesRepository.findOrNull(); Whitelist whitelist = props.getBlogHtmlPolicy().getWhitelist(); if (whitelist != null) { ret = Jsoup.clean(ret, whitelist); } } return ret; } /** * Create an Atom enclosure element for the resource (usually podcast or other * multimedia) at the specified URL. * * @param url web URL where the resource is located. * @return AtomEnclosure element for the resource */ public AtomEnclosure generateEnclosure(String url) { if (url == null || url.trim().length() == 0) { return null; } AtomEnclosure resource; try { HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); con.setRequestMethod("HEAD"); int response = con.getResponseCode(); String message = con.getResponseMessage(); if (response != 200) { // Bad Response log.debug("Mediacast error {}:{} from url {}", response, message, url); throw new IllegalArgumentException("entryEdit.mediaCastResponseError"); } else { String contentType = con.getContentType(); long length = con.getContentLength(); if (contentType == null || length == -1) { // Incomplete log.debug("Response valid, but contentType or length is invalid"); throw new IllegalArgumentException("entryEdit.mediaCastLacksContentTypeOrLength"); } resource = new AtomEnclosure(url, contentType, length); log.debug("Valid mediacast resource = {}", resource.toString()); } } catch (MalformedURLException mfue) { // Bad URL log.debug("Malformed MediaCast url: {}", url); throw new IllegalArgumentException("entryEdit.mediaCastUrlMalformed", mfue); } catch (Exception e) { // Check Failed log.error("ERROR while checking MediaCast URL: {}: {}", url, e.getMessage()); throw new IllegalArgumentException("entryEdit.mediaCastFailedFetchingInfo", e); } return resource; } /** * Turn off further notifications to a blog commenter who requested "notify me" * for future comments for a particular weblog entry. * @param commentId weblog entry id where commenter commented * @return Pair <String, Boolean> = String is blog entry title or null if not found, * Boolean is true if subscribed user found (& hence unsubscribed), false if user * not found or blog entry not found. */ @Transactional public Pair<String, Boolean> stopNotificationsForCommenter(String commentId) { boolean found = false; String blogEntryTitle = null; WeblogEntryComment commentWithUnsubscribingUser = weblogEntryCommentRepository.findByIdOrNull(commentId); if (commentWithUnsubscribingUser != null) { // get entry WeblogEntry entry = commentWithUnsubscribingUser.getWeblogEntry(); blogEntryTitle = entry.getTitle(); // turn off notify on all comments for this entry by user List<WeblogEntryComment> comments = weblogEntryCommentRepository.findByWeblogEntry(entry); for (WeblogEntryComment comment : comments) { if (comment.getNotify() && comment.getEmail().equalsIgnoreCase(commentWithUnsubscribingUser.getEmail())) { comment.setNotify(false); weblogEntryCommentRepository.save(comment); found = true; } } } return Pair.of(blogEntryTitle, found); } public ValidationResult makeAkismetCall(String apiRequestBody) throws IOException { if (!StringUtils.isBlank(akismetApiKey)) { URL url = new URL("http://" + akismetApiKey + ".rest.akismet.com/1.1/comment-check"); URLConnection conn = url.openConnection(); conn.setDoOutput(true); conn.setRequestProperty("User_Agent", "TightBlog"); conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded;charset=utf8"); conn.setRequestProperty("Content-length", Integer.toString(apiRequestBody.length())); OutputStreamWriter osr = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8); osr.write(apiRequestBody, 0, apiRequestBody.length()); osr.flush(); osr.close(); try (InputStreamReader isr = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr)) { String response = br.readLine(); if ("true".equals(response)) { if ("discard".equalsIgnoreCase(conn.getHeaderField("X-akismet-pro-tip"))) { return ValidationResult.BLATANT_SPAM; } return ValidationResult.SPAM; } } } return ValidationResult.NOT_SPAM; } }