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