View Javadoc
1   /*
2    * Copyright (C) 2003-2008 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.wcm.link;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.List;
22  
23  import javax.jcr.Node;
24  import javax.jcr.NodeIterator;
25  import javax.jcr.Property;
26  import javax.jcr.Session;
27  import javax.jcr.Value;
28  import javax.jcr.ValueFactory;
29  import javax.jcr.query.Query;
30  import javax.jcr.query.QueryManager;
31  import javax.jcr.query.QueryResult;
32  
33  import org.apache.commons.httpclient.HttpClient;
34  import org.apache.commons.httpclient.SimpleHttpConnectionManager;
35  import org.apache.commons.httpclient.methods.GetMethod;
36  import org.exoplatform.container.xml.InitParams;
37  import org.exoplatform.container.xml.PropertiesParam;
38  import org.exoplatform.services.cache.CacheService;
39  import org.exoplatform.services.cache.ExoCache;
40  import org.exoplatform.services.jcr.RepositoryService;
41  import org.exoplatform.services.jcr.core.ManageableRepository;
42  import org.exoplatform.services.jcr.ext.common.SessionProvider;
43  import org.exoplatform.services.log.ExoLogger;
44  import org.exoplatform.services.log.Log;
45  import org.exoplatform.services.wcm.core.NodeLocation;
46  import org.exoplatform.services.wcm.core.WCMConfigurationService;
47  import org.exoplatform.services.wcm.portal.LivePortalManagerService;
48  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
49  
50  /**
51   * Created by The eXo Platform SAS Author : Phan Le Thanh Chuong
52   * chuong_phan@exoplatform.com Aug 6, 2008
53   */
54  public class LiveLinkManagerServiceImpl implements LiveLinkManagerService {
55  
56    /** The broken links cache. */
57    private ExoCache<String, List<String>>                 brokenLinksCache;
58  
59    /** The configuration service. */
60    private WCMConfigurationService  configurationService;
61  
62    /** The repository service. */
63    private RepositoryService        repositoryService;
64  
65    /** The live portal manager service. */
66    private LivePortalManagerService livePortalManagerService;
67  
68    /** The internal server path. */
69    private String internalServerPath;
70  
71    /** The log. */
72    final private static Log LOG = ExoLogger.getLogger(LiveLinkManagerServiceImpl.class.getName());
73  
74    private final static String CACHE_NAME = "ecms.LiveLinkManagerService";
75  
76    /**
77     * Instantiates a new live link manager service impl.
78     *
79     * @param configurationService the configuration service
80     * @param repositoryService the repository service
81     * @param livePortalManagerService the live portal manager service
82     * @param cacheService the cache service
83     * @param initParams the init params
84     *
85     * @throws Exception the exception
86     */
87    public LiveLinkManagerServiceImpl(
88        WCMConfigurationService   configurationService,
89        RepositoryService         repositoryService,
90        LivePortalManagerService  livePortalManagerService,
91        CacheService              cacheService,
92        InitParams initParams) throws Exception {
93      this.configurationService = configurationService;
94      this.repositoryService = repositoryService;
95      this.livePortalManagerService = livePortalManagerService;
96      this.brokenLinksCache = cacheService.getCacheInstance(CACHE_NAME);
97  
98      PropertiesParam propertiesParam = initParams.getPropertiesParam("server.config");
99      String scheme = propertiesParam.getProperty("scheme");
100     String hostName = propertiesParam.getProperty("hostName");
101     String port = propertiesParam.getProperty("port");
102     StringBuilder builder = new StringBuilder();
103     builder.append(scheme).append("://").append(hostName).append(":").append(port);
104     internalServerPath = builder.toString();
105   }
106 
107   /* (non-Javadoc)
108    * @see org.exoplatform.services.wcm.link.LiveLinkManagerService#getBrokenLinks(java.lang.String)
109    */
110   public List<LinkBean> getBrokenLinks(String portalName) throws Exception {
111     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
112     Node portal = livePortalManagerService.getLivePortal(sessionProvider, portalName);
113     String path = portal.getPath();
114     Session session = portal.getSession();
115     List<LinkBean> listBrokenLinks = new ArrayList<LinkBean>();
116     QueryManager queryManager = session.getWorkspace().getQueryManager();
117     Query query = queryManager.createQuery("select * from exo:webContent where jcr:path like '" + path + "/%'", Query.SQL);
118     QueryResult results = query.execute();
119     NodeIterator iter = results.getNodes();
120     for (;iter.hasNext();) {
121       Node webContent = iter.nextNode();
122       List<String> listBrokenUrls = getBrokenLinks(webContent);
123       for (String brokenUrl : listBrokenUrls) {
124         LinkBean linkBean = new LinkBean(brokenUrl, LinkBean.STATUS_BROKEN);
125         listBrokenLinks.add(linkBean);
126       }
127     }
128     return listBrokenLinks;
129   }
130 
131   /* (non-Javadoc)
132    * @see org.exoplatform.services.wcm.link.LiveLinkManagerService#getBrokenLinks(javax.jcr.Node)
133    */
134   public List<String> getBrokenLinks(Node webContent) throws Exception {
135     List<String> listBrokenUrls = (List<String>) brokenLinksCache.get(webContent.getUUID());
136     if (listBrokenUrls == null || listBrokenUrls.size() == 0) {
137       listBrokenUrls = new ArrayList<String>();
138       if (webContent.hasProperty("exo:links")) {
139         for (Value value : webContent.getProperty("exo:links").getValues()) {
140           String link = value.getString();
141           LinkBean linkBean = LinkBean.parse(link);
142           if (linkBean.isBroken()) {
143             listBrokenUrls.add(linkBean.getUrl());
144           }
145         }
146         brokenLinksCache.put(webContent.getUUID(), listBrokenUrls);
147       }
148     }
149     return listBrokenUrls;
150   }
151 
152   /* (non-Javadoc)
153    * @see org.exoplatform.services.wcm.link.LiveLinkManagerService#validateLink()
154    */
155   public void updateLinks() throws Exception {
156     try {
157       Collection<NodeLocation> nodeLocationCollection = configurationService.getAllLivePortalsLocation();
158       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
159       Session session = null;
160       for (NodeLocation nodeLocation : nodeLocationCollection) {
161         String workspace = nodeLocation.getWorkspace();
162         String path = nodeLocation.getPath();
163         ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
164         session = sessionProvider.getSession(workspace, manageableRepository);
165         updateLinkStatus(session, "select * from exo:linkable where jcr:path like '" + path + "/%'");
166       }
167     } catch (Exception e) {
168       if (LOG.isErrorEnabled()) {
169         LOG.error("Error when perform updateLinks: ", e);
170       }
171     }
172   }
173 
174   /* (non-Javadoc)
175    * @see org.exoplatform.services.wcm.link.LiveLinkManagerService#validateLink(java.lang.String)
176    */
177   public void updateLinks(String portalName) throws Exception {
178     try {
179       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
180       Node portal = livePortalManagerService.getLivePortal(sessionProvider, portalName);
181       String path = portal.getPath();
182       Session session = portal.getSession();
183       updateLinkStatus(session, "select * from exo:linkable where jcr:path like '" + path + "/%'");
184     } catch (Exception e) {
185       if (LOG.isErrorEnabled()) {
186         LOG.error("Error when perform updateLinks: ", e);
187       }
188     }
189   }
190 
191   /**
192    * Update link status.
193    *
194    * @param session the session
195    * @param queryCommand the query command
196    *
197    * @throws Exception the exception
198    */
199   private void updateLinkStatus(Session session, String queryCommand) throws Exception{
200     List<String> listBrokenLinks = new ArrayList<String>();
201     ValueFactory valueFactory = session.getValueFactory();
202     QueryManager queryManager = session.getWorkspace().getQueryManager();
203     Query query = queryManager.createQuery(queryCommand, Query.SQL);
204     QueryResult results = query.execute();
205     NodeIterator iter = results.getNodes();
206     for (; iter.hasNext();) {
207       Node webContent = iter.nextNode();
208       if (!webContent.isCheckedOut() || webContent.isLocked()
209           || (webContent.isCheckedOut() && !webContent.getParent().isCheckedOut())) {
210         continue;
211       }
212       Property links = webContent.getProperty("exo:links");
213       Value[] oldValues = links.getValues();
214       Value[] newValues = new Value[oldValues.length];
215       for (int iValues = 0; iValues < oldValues.length; iValues++) {
216         String oldLink = oldValues[iValues].getString();
217         if (!oldLink.equals("")) {
218           LinkBean linkBean = LinkBean.parse(oldLink);
219           String oldUrl = linkBean.getUrl();
220           String oldStatus = getLinkStatus(oldUrl);
221           String updatedLink = new LinkBean(oldUrl, oldStatus).toString();
222           if (LOG.isInfoEnabled()) {
223             LOG.info(updatedLink);
224           }
225           newValues[iValues] = valueFactory.createValue(updatedLink);
226           if (oldStatus.equals(LinkBean.STATUS_BROKEN)) {
227             listBrokenLinks.add(oldUrl);
228           }
229         }
230       }
231       webContent.setProperty("exo:links",newValues);
232       brokenLinksCache.put(webContent.getUUID(), listBrokenLinks);
233     }
234     session.save();
235   }
236 
237   /**
238    * Gets the link status.
239    *
240    * @param strUrl the str url
241    *
242    * @return the link status
243    */
244   private String getLinkStatus(String strUrl) {
245     try {
246       String fullUrl = strUrl;
247       if(strUrl.startsWith("/")) {
248         fullUrl = internalServerPath + strUrl;
249       }
250       fullUrl = fullUrl.replaceAll(" ","%20");
251       HttpClient httpClient = new HttpClient(new SimpleHttpConnectionManager());
252       GetMethod getMethod = new GetMethod(fullUrl);
253       if(httpClient.executeMethod(getMethod) == 200) {
254         return LinkBean.STATUS_ACTIVE;
255       }
256       return LinkBean.STATUS_BROKEN;
257     } catch (Exception e) {
258       if (LOG.isInfoEnabled()) {
259         LOG.info("URL Link: \"" + strUrl + "\" is broken");
260       }
261       return LinkBean.STATUS_BROKEN;
262     }
263   }
264 
265   /* (non-Javadoc)
266    * @see org.exoplatform.services.wcm.link.LiveLinkManagerService#extractLinks(org.exoplatform.services.html.HTMLDocument)
267    */
268   public List<String> extractLinks(Node htmlFile) throws Exception {
269     String htmlData = htmlFile.getNode("jcr:content").getProperty("jcr:data").getString();
270     List<String> listHyperlink = new ArrayList<String>();
271     HTMLLinkExtractor htmlLinkExtractor = new HTMLLinkExtractor();
272     List<HTMLLinkExtractor.HtmlLink> htmlLinks = htmlLinkExtractor.grabHTMLLinks(htmlData);
273     for (HTMLLinkExtractor.HtmlLink link : htmlLinks) {
274       if (!listHyperlink.contains(link.toString()))
275         listHyperlink.add(link.toString());
276     }
277     return listHyperlink;
278   }
279 
280 
281   /* (non-Javadoc)
282    * @see org.exoplatform.services.wcm.link.LiveLinkManagerService#updateLinks(javax.jcr.Node, java.util.List)
283    */
284   public void updateLinkDataForNode(Node webContent, List<String> newLinks) throws Exception {
285     ValueFactory valueFactory = webContent.getSession().getValueFactory();
286     if (webContent.canAddMixin("exo:linkable")) {
287       webContent.addMixin("exo:linkable");
288     }
289     // get old link from exo:links property
290     List<String> listExtractedLink = new ArrayList<String>();
291     if (webContent.hasProperty("exo:links")) {
292       Property property = webContent.getProperty("exo:links");
293       for (Value value : property.getValues()) {
294         listExtractedLink.add(value.getString());
295       }
296     }
297     // compare, remove old link, add new link, create new List
298     List<String> listResult = new ArrayList<String>();
299 
300     for (String extractedLink : listExtractedLink) {
301       for (String newUrl : newLinks) {
302         if (LinkBean.parse(extractedLink).getUrl().equals(newUrl)) {
303           listResult.add(extractedLink);
304         }
305       }
306     }
307     List<String> listTemp = new ArrayList<String>();
308     listTemp.addAll(newLinks);
309 
310     for (String newUrl : newLinks) {
311       for (String extractedLink : listExtractedLink) {
312         if (newUrl.equals(LinkBean.parse(extractedLink).getUrl())) {
313           listTemp.set(newLinks.indexOf(newUrl), "");
314         }
315       }
316     }
317 
318     for (String strTemp : listTemp) {
319       if (!strTemp.equals("")) {
320         listResult.add(strTemp);
321       }
322     }
323 
324     // Create an array of value to add to exo:links property
325     Value[] values = new Value[listResult.size()];
326     for(String url: listResult) {
327       if (url.indexOf(LinkBean.STATUS) < 0) {
328         LinkBean linkBean = new LinkBean(url, LinkBean.STATUS_UNCHECKED);
329         values[listResult.indexOf(url)] = valueFactory.createValue(linkBean.toString());
330       } else {
331         values[listResult.indexOf(url)] = valueFactory.createValue(url);
332       }
333     }
334     webContent.setProperty("exo:links", values);
335     webContent.save();
336   }
337 
338 }