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