001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.blogs.util;
016    
017    import com.liferay.ibm.icu.util.Calendar;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.servlet.HttpHeaders;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.HtmlUtil;
024    import com.liferay.portal.kernel.util.Http;
025    import com.liferay.portal.kernel.util.HttpUtil;
026    import com.liferay.portal.kernel.util.ReleaseInfo;
027    import com.liferay.portal.kernel.util.Tuple;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.xmlrpc.Response;
030    import com.liferay.portal.kernel.xmlrpc.XmlRpcException;
031    import com.liferay.portal.kernel.xmlrpc.XmlRpcUtil;
032    import com.liferay.portal.util.PropsValues;
033    import com.liferay.portal.xml.StAXReaderUtil;
034    
035    import java.util.ArrayList;
036    import java.util.Collections;
037    import java.util.Date;
038    import java.util.List;
039    import java.util.Map;
040    
041    import javax.xml.stream.XMLInputFactory;
042    import javax.xml.stream.XMLStreamReader;
043    
044    import net.htmlparser.jericho.Source;
045    import net.htmlparser.jericho.StartTag;
046    
047    /**
048     * @author Alexander Chow
049     */
050    public class LinkbackProducerUtil {
051    
052            public static void sendPingback(String sourceUri, String targetUri)
053                    throws Exception {
054    
055                    _pingbackQueue.add(new Tuple(new Date(), sourceUri, targetUri));
056            }
057    
058            public static synchronized void sendQueuedPingbacks()
059                    throws XmlRpcException {
060    
061                    Calendar cal = Calendar.getInstance();
062    
063                    cal.add(Calendar.MINUTE, -1);
064    
065                    Date expiration = cal.getTime();
066    
067                    while (!_pingbackQueue.isEmpty()) {
068                            Tuple tuple = _pingbackQueue.get(0);
069    
070                            Date time = (Date)tuple.getObject(0);
071    
072                            if (time.before(expiration)) {
073                                    _pingbackQueue.remove(0);
074    
075                                    String sourceUri = (String)tuple.getObject(1);
076                                    String targetUri = (String)tuple.getObject(2);
077    
078                                    String serverUri = _discoverPingbackServer(targetUri);
079    
080                                    if (Validator.isNull(serverUri)) {
081                                            continue;
082                                    }
083    
084                                    if (_log.isInfoEnabled()) {
085                                            _log.info(
086                                                    "XML-RPC pingback " + serverUri + ", source " +
087                                                            sourceUri + ", target " + targetUri);
088                                    }
089    
090                                    Response response = XmlRpcUtil.executeMethod(
091                                            serverUri, "pingback.ping",
092                                            new Object[] {sourceUri, targetUri});
093    
094                                    if (_log.isInfoEnabled()) {
095                                            _log.info(response.toString());
096                                    }
097                            }
098                            else {
099                                    break;
100                            }
101                    }
102            }
103    
104            public static boolean sendTrackback(
105                            String trackback, Map<String, String> parts)
106                    throws Exception {
107    
108                    if (_log.isInfoEnabled()) {
109                            _log.info("Pinging trackback " + trackback);
110                    }
111    
112                    Http.Options options = new Http.Options();
113    
114                    if (_HTTP_HEADER_VERSION_VERBOSITY_DEFAULT) {
115                    }
116                    else if (_HTTP_HEADER_VERSION_VERBOSITY_PARTIAL) {
117                            options.addHeader(HttpHeaders.USER_AGENT, ReleaseInfo.getName());
118                    }
119                    else {
120                            options.addHeader(
121                                    HttpHeaders.USER_AGENT, ReleaseInfo.getServerInfo());
122                    }
123    
124                    options.setLocation(trackback);
125                    options.setParts(parts);
126                    options.setPost(true);
127    
128                    String xml = HttpUtil.URLtoString(options);
129    
130                    if (_log.isDebugEnabled()) {
131                            _log.debug(xml);
132                    }
133    
134                    String error = xml;
135    
136                    XMLStreamReader xmlStreamReader = null;
137    
138                    try {
139                            XMLInputFactory xmlInputFactory =
140                                    StAXReaderUtil.getXMLInputFactory();
141    
142                            xmlStreamReader = xmlInputFactory.createXMLStreamReader(
143                                    new UnsyncStringReader(xml));
144    
145                            xmlStreamReader.nextTag();
146                            xmlStreamReader.nextTag();
147    
148                            String name = xmlStreamReader.getLocalName();
149    
150                            if (name.equals("error")) {
151                                    int status = GetterUtil.getInteger(
152                                            xmlStreamReader.getElementText(), 1);
153    
154                                    if (status == 0) {
155                                            if (_log.isInfoEnabled()) {
156                                                    _log.info("Trackback accepted");
157                                            }
158    
159                                            return true;
160                                    }
161    
162                                    xmlStreamReader.nextTag();
163    
164                                    name = xmlStreamReader.getLocalName();
165    
166                                    if (name.equals("message")) {
167                                            error = xmlStreamReader.getElementText();
168                                    }
169                            }
170                    }
171                    finally {
172                            if (xmlStreamReader != null) {
173                                    try {
174                                            xmlStreamReader.close();
175                                    }
176                                    catch (Exception e) {
177                                    }
178                            }
179                    }
180    
181                    _log.error(
182                            "Error while pinging trackback at " + trackback + ": " + error);
183    
184                    return false;
185            }
186    
187            private static String _discoverPingbackServer(String targetUri) {
188                    String serverUri = null;
189    
190                    try {
191                            Http.Options options = new Http.Options();
192    
193                            if (_HTTP_HEADER_VERSION_VERBOSITY_DEFAULT) {
194                            }
195                            else if (_HTTP_HEADER_VERSION_VERBOSITY_PARTIAL) {
196                                    options.addHeader(
197                                            HttpHeaders.USER_AGENT, ReleaseInfo.getName());
198                            }
199                            else {
200                                    options.addHeader(
201                                            HttpHeaders.USER_AGENT, ReleaseInfo.getServerInfo());
202                            }
203    
204                            options.setLocation(targetUri);
205                            options.setHead(true);
206    
207                            HttpUtil.URLtoByteArray(options);
208    
209                            Http.Response response = options.getResponse();
210    
211                            serverUri = response.getHeader("X-Pingback");
212                    }
213                    catch (Exception e) {
214                            _log.error("Unable to call HEAD of " + targetUri, e);
215                    }
216    
217                    if (Validator.isNotNull(serverUri)) {
218                            return serverUri;
219                    }
220    
221                    try {
222                            Source clientSource = new Source(HttpUtil.URLtoString(targetUri));
223    
224                            List<StartTag> startTags = clientSource.getAllStartTags("link");
225    
226                            for (StartTag startTag : startTags) {
227                                    String rel = startTag.getAttributeValue("rel");
228    
229                                    if (rel.equalsIgnoreCase("pingback")) {
230                                            String href = startTag.getAttributeValue("href");
231    
232                                            serverUri = HtmlUtil.escape(href);
233    
234                                            break;
235                                    }
236                            }
237                    }
238                    catch (Exception e) {
239                            _log.error("Unable to call GET of " + targetUri, e);
240                    }
241    
242                    return serverUri;
243            }
244    
245            private static final boolean _HTTP_HEADER_VERSION_VERBOSITY_DEFAULT =
246                    PropsValues.HTTP_HEADER_VERSION_VERBOSITY.equalsIgnoreCase(
247                            ReleaseInfo.getName());
248    
249            private static final boolean _HTTP_HEADER_VERSION_VERBOSITY_PARTIAL =
250                    PropsValues.HTTP_HEADER_VERSION_VERBOSITY.equalsIgnoreCase("partial");
251    
252            private static Log _log = LogFactoryUtil.getLog(LinkbackProducerUtil.class);
253    
254            private static List<Tuple> _pingbackQueue = Collections.synchronizedList(
255                    new ArrayList<Tuple>());
256    
257    }