//------------------------------------------------------------------------------ // Copyright (c) 2005, 2006 IBM Corporation and others. // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // which accompanies this distribution, and is available at // http://www.eclipse.org/legal/epl-v10.html // // Contributors: // IBM Corporation - initial implementation //------------------------------------------------------------------------------ package org.eclipse.epf.publishing.services; import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.runtime.Platform; import org.eclipse.epf.common.utils.Timer; import org.eclipse.epf.library.layout.DefaultContentValidator; import org.eclipse.epf.library.layout.LinkInfo; import org.eclipse.epf.library.layout.util.XmlElement; import org.eclipse.epf.library.util.LibraryUtil; import org.eclipse.epf.library.util.ResourceHelper; import org.eclipse.epf.publishing.PublishingPlugin; import org.eclipse.epf.publishing.PublishingResources; import org.eclipse.epf.publishing.util.http.HttpResponse; import org.eclipse.epf.publishing.util.http.HttpUtil; import org.eclipse.epf.uma.ContentCategory; import org.eclipse.epf.uma.MethodConfiguration; import org.eclipse.epf.uma.MethodElement; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import com.ibm.icu.util.Calendar; /** * content validator used for publishing. This class will be responsible for * validating the content to be published, fixing problems such as links in the * content, and logging information about missing elements, missing resources, * etc. * * @author Jinhua Xi * @since 1.0 * */ public class PublishingContentValidator extends DefaultContentValidator { private boolean debug = PublishingPlugin.getDefault().isDebugging(); // private boolean showExtraInfoForDescriptors = false; // private boolean showRelatedDescriptors = false; protected PublishHTMLOptions options = null; class InvalidExternalLinkInfo { public MethodElement owner; public String url; public String message; public InvalidExternalLinkInfo(MethodElement owner, String url, String message) { this.owner = owner; this.url = url; this.message = message; } } class MissingReference { public MethodElement owner; public MethodElement refElement; public String guid; public String linkedText; public MissingReference(MethodElement owner, MethodElement refElement) { this.owner = owner; this.refElement = refElement; } public MissingReference(MethodElement owner, String guid, String linkedText) { this.owner = owner; this.guid = guid; this.linkedText = linkedText; } } class MissingResource { public MethodElement owner; public File resourceFile; public String url; public MissingResource(MethodElement owner, File resourceFile, String url) { this.owner = owner; this.resourceFile = resourceFile; this.url = url; } } static final String LOGS_FOLDER = "logs"; //$NON-NLS-1$ static final String ERROR_LOG_FILENAME = "error.log"; //$NON-NLS-1$ static final String WARNING_LOG_FILENAME = "warning.log"; //$NON-NLS-1$ static final String INFO_LOG_FILENAME = "info.log"; //$NON-NLS-1$ static final String CORE_NET_BUNDLE = "org.eclipse.core.net"; //$NON-NLS-1$ protected File logPath; protected boolean validateExternalLinks = false; protected List invalidExternalLinks = new ArrayList(); // cache the valided external links to avoid multiple checking protected List validatedExternalLinks = new ArrayList(); protected List missingReferences = new ArrayList(); protected List missingResources = new ArrayList(); protected List discardedElements = new ArrayList(); protected List validCategories = new ArrayList(); protected long publishing_start = 0; protected long time_for_external_link_checking = 0; // collect the elements referenced by the published contents so we can // publish them // this will be the elements to be published private List referencedElements = new ArrayList(); // published elements private List publishedElements = new ArrayList(); // this is the default target element for the content validator // set this before publishing the element and set to null after the // publishign is done protected MethodElement defaultTarget = null; /** * consructor * * @param pubDir * String * @param validateExternalLinks * boolean */ public PublishingContentValidator(String pubDir, PublishHTMLOptions options) { super(pubDir); this.options = options; this.validateExternalLinks = options.isCheckExternalLinks(); this.logPath = new File(pubDir, LOGS_FOLDER); super.info = getStream(INFO_LOG_FILENAME); super.warning = getStream(WARNING_LOG_FILENAME); super.error = getStream(ERROR_LOG_FILENAME); // set the start time publishing_start = Calendar.getInstance().getTimeInMillis(); } /** * dispose the object */ public void dispose() { invalidExternalLinks.clear(); validatedExternalLinks.clear(); missingReferences.clear(); missingResources.clear(); discardedElements.clear(); validCategories.clear(); referencedElements.clear(); publishedElements.clear(); info.close(); warning.close(); error.close(); } protected PrintStream getStream(String fileName) { try { File f = new File(logPath, fileName); File dir = f.getParentFile(); dir.mkdirs(); if (!f.exists()) { f.createNewFile(); } return new PrintStream(new FileOutputStream(f), true); } catch (Exception e) { } return null; } private IProxyService getProxyService() { Bundle bundle = Platform.getBundle(CORE_NET_BUNDLE); if (bundle != null) { BundleContext ctx = bundle.getBundleContext(); String name = IProxyService.class.getName(); ServiceReference ref = ctx.getServiceReference(name); if (ref != null) return (IProxyService) bundle.getBundleContext() .getService(ref); } return null; } /** * validate the link attributes fro the element. * * @param owner * MethodElement the owner element * @param attributes * String the attributes in the link * @param text * String the text allow with the link * @param config * MethodConfiguration * * @return LinkInfo */ public LinkInfo validateLink(MethodElement owner, String attributes, String text, MethodConfiguration config, String tag) { LinkInfo info = super .validateLink(owner, attributes, text, config, tag); if (validateExternalLinks) { String url = info.getUrl(); if ((url != null) && ResourceHelper.isExternalLink(url) && !url.startsWith("ftp://")) //$NON-NLS-1$ { if (!validatedExternalLinks.contains(url)) { Timer t = new Timer(); IProxyService proxyService = getProxyService(); if (proxyService.isProxiesEnabled()) { IProxyData proxy = null; boolean accessible = false; boolean useProxy = false; String exceptionMessage = null; String[] proxyTypes = { IProxyData.HTTP_PROXY_TYPE, IProxyData.HTTPS_PROXY_TYPE, IProxyData.SOCKS_PROXY_TYPE }; for (String proxyType : proxyTypes) { // access external link by using different proxys proxy = proxyService.getProxyData(proxyType); if ((proxy.getHost()==null)||proxy.getPort()==-1) { continue; } try { HttpResponse resp = HttpUtil.doGet(url, null, 6000, proxy); // timeout useProxy = true; accessible = true; break; } catch (java.net.UnknownHostException e) { } catch (Exception e) { if (exceptionMessage == null) { exceptionMessage = proxy.getHost() + ":" //$NON-NLS-1$ + proxy.getPort() + "[" + proxyType //$NON-NLS-1$ + "]:" + e.getMessage(); //$NON-NLS-1$ } else { exceptionMessage += ";" + proxy.getHost() //$NON-NLS-1$ + ":" + proxy.getPort() + "[" //$NON-NLS-1$ //$NON-NLS-2$ + proxyType + "]:" + e.getMessage(); //$NON-NLS-1$ } } } if (useProxy) { if (!accessible) { if (exceptionMessage != null) { logInvalidExternalLink(owner, url, exceptionMessage); } else { logInvalidExternalLink(owner, url, null); } } } else // connect directly { try { HttpResponse resp = HttpUtil.doGet(url, null, 6000); // timeout } catch (java.net.UnknownHostException e) { logInvalidExternalLink(owner, url, null); } catch (Exception e) { logInvalidExternalLink(owner, url, e.getMessage()); } } } else { try { HttpResponse resp = HttpUtil.doGet(url, null, 6000); // timeout // System.out // .println(time // + " mini-seconds querying Url '" + url + "', // return // status=" + resp.getStatus()); //$NON-NLS-1$ // //$NON-NLS-2$ } catch (java.net.UnknownHostException e) { logInvalidExternalLink(owner, url, null); } catch (Exception e) { logInvalidExternalLink(owner, url, e.getMessage()); } } t.stop(); time_for_external_link_checking += t.getTime(); // cache it validatedExternalLinks.add(url); // do we need to log the info so that user know what // external // urls are referenced in the content? logInfo(owner, NLS.bind( PublishingResources.externalUrl_msg, new Object[] { new Integer(t.getTime()), url })); } } } return info; } /** * log missing reference. * * @param owner * MethodElement * @param refElement * MethodElement the missing element */ public void logMissingReference(MethodElement owner, MethodElement refElement) { super.logMissingReference(owner, refElement); missingReferences.add(new MissingReference(owner, refElement)); } /** * log missing reference * * @param owner * M<ethodElement * @param guid * String the guid of the missing element * @param linkedText * String the linked text. */ public void logMissingReference(MethodElement owner, String guid, String linkedText) { super.logMissingReference(owner, guid, linkedText); missingReferences.add(new MissingReference(owner, guid, linkedText)); } /** * log missing resource. * * @param owner * MethodElement * @param resourceFile * File * @param url * String */ public void logMissingResource(MethodElement owner, File resourceFile, String url) { super.logMissingResource(owner, resourceFile, url); missingResources.add(new MissingResource(owner, resourceFile, url)); } /** * log invalid external link * * @param owner * @param url * String * @param message * String */ public void logInvalidExternalLink(MethodElement owner, String url, String message) { super.logInvalidExternalLink(owner, url, message); invalidExternalLinks.add(new InvalidExternalLinkInfo(owner, url, message)); } /** * get report about the content validation. * * @return XmlElement */ public XmlElement getReport() { XmlElement reportXml = new XmlElement("validatorInfo"); //$NON-NLS-1$ if (invalidExternalLinks.size() > 0) { String msg = time_for_external_link_checking / 1000 + " seconds validating external links"; //$NON-NLS-1$ System.out.println(msg); logInfo(msg); XmlElement invalidExternalLinksXml = reportXml .newChild("invalidExternalLinks"); //$NON-NLS-1$ for (Iterator it = invalidExternalLinks.iterator(); it.hasNext();) { InvalidExternalLinkInfo info = (InvalidExternalLinkInfo) it .next(); invalidExternalLinksXml .newChild("entry") //$NON-NLS-1$ .setAttribute("url", info.url) //$NON-NLS-1$ .setAttribute( "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)) //$NON-NLS-1$ //$NON-NLS-2$ .setAttribute("message", info.message); //$NON-NLS-1$ } } if (missingReferences.size() > 0) { XmlElement invalidReferencesXml = reportXml .newChild("invalidReferences"); //$NON-NLS-1$ XmlElement missingReferencesXml = reportXml .newChild("missingReferences"); //$NON-NLS-1$ for (Iterator it = missingReferences.iterator(); it.hasNext();) { MissingReference info = (MissingReference) it.next(); if (info.refElement == null) { invalidReferencesXml .newChild("entry") //$NON-NLS-1$ .setAttribute("element", info.linkedText) //$NON-NLS-1$ .setAttribute("guid", info.guid) //$NON-NLS-1$ .setAttribute( "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)); //$NON-NLS-1$ //$NON-NLS-2$ } else { missingReferencesXml .newChild("entry") //$NON-NLS-1$ .setAttribute( "element", (info.refElement == null) ? "" : LibraryUtil.getLocalizeTypeName(info.refElement)) //$NON-NLS-1$ //$NON-NLS-2$ .setAttribute("guid", info.refElement.getGuid()) //$NON-NLS-1$ .setAttribute( "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)); //$NON-NLS-1$ //$NON-NLS-2$ } } } if (missingResources.size() > 0) { XmlElement missingResourcesXml = reportXml .newChild("missingResources"); //$NON-NLS-1$ for (Iterator it = missingResources.iterator(); it.hasNext();) { MissingResource info = (MissingResource) it.next(); missingResourcesXml .newChild("entry") //$NON-NLS-1$ .setAttribute("url", info.url) //$NON-NLS-1$ .setAttribute( "resource", (info.resourceFile == null) ? "" : info.resourceFile.getPath()) //$NON-NLS-1$ //$NON-NLS-2$ .setAttribute( "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)); //$NON-NLS-1$ //$NON-NLS-2$ } } long publishing_time = (Calendar.getInstance().getTimeInMillis() - publishing_start) / 1000; int minutes = (int) publishing_time / 60; int seconds = (int) publishing_time - minutes * 60; logInfo("Publishing time: " + minutes + " minutes " + seconds + " seconds"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return reportXml; } /** * add a category that should be published. * * @param e * ContentCategory */ public void addValidCategory(ContentCategory e) { if (!validCategories.contains(e)) { validCategories.add(e); } } /** * check if the element is discarded or not discarded elements will be * treated as out side the configursation * * @param owner * MethodElement the owner of the element * @param feature * Object EStructuralFeature or OppositeFeature * @param e * MethodElement the element to be checked * * @return boolean */ public boolean isDiscarded(MethodElement owner, Object feature, MethodElement e) { if (discardedElements.contains(e)) { return true; } // if the element is a ContentCategory and is not discarded if (e instanceof ContentCategory) { if (validCategories.contains(e)) { return false; } // otherwise, check if it should be discarded or not // TODO // for now, discard all referenced content categories if they are // not included in the publishing view. // NO!!!!!!!!!!!! This will lead to a lot of broken links and // missing element // TOO strong limitation. Let open it for now // Publishing:Overview page in published website // have broken links to BM // return true; return false; } return false; } /** * add a referenced element * * @param owner * MethodElement * @param e * MethodElement */ public void addReferencedElement(MethodElement owner, MethodElement e) { if (e == null) { return; } // don't add discarded elements if (isDiscarded(owner, null, e)) { if (debug) { System.out .println("Element is discarded: " + LibraryUtil.getLocalizeTypeName(e)); //$NON-NLS-1$ } return; } if (e != null && !referencedElements.contains(e) && !publishedElements.contains(e)) { referencedElements.add(e); logReference(owner, e); } } /** * log a refernece * * @param owner * MethodElement * @param e * MethodElement */ public void logReference(MethodElement owner, MethodElement e) { if (debug) { System.out .println("--- Referenece Element Added: " + LibraryUtil.getLocalizeTypeName(e)); //$NON-NLS-1$ } } /** * remove element from referenced list * * @param e * MethodElement */ public void removeReferencedElement(MethodElement e) { if (referencedElements.contains(e)) { referencedElements.remove(e); if (debug) { System.out .println("--- Reference Element Removed: " + LibraryUtil.getLocalizeTypeName(e)); //$NON-NLS-1$ } } } /** * get all the referenced elements * * @return List */ public List getReferencedElements() { return referencedElements; } /** * aet the discarded element for this publication. If an element is * discarded, it should not be published and link to it should be link to * mising element page * * @param e * MethodElement */ public void setDiscardedElement(MethodElement e) { if (e == null) { return; } if (!discardedElements.contains(e)) { discardedElements.add(e); } // if th fdiscarded element is in the reference list, remove it as well removeReferencedElement(e); } /** * check if an elenment is referenced or not. * * @param e * MethodElement * @return boolean */ public boolean isReferencedElement(MethodElement e) { return (e != null) && referencedElements.contains(e); } /** * get the published elements * * @return List */ public List getPublishedElements() { return publishedElements; } /** * set the default target for the referenced elements * * @param target * MethodElement */ public void setTargetElement(MethodElement target) { this.defaultTarget = target; } /** * check if there is a closure or not * * @return boolean */ public boolean hasClosure() { return false; } /** * check if an element is in closure or not. * * @param e * MethodElement * @return boolean */ public boolean inClosure(MethodElement e) { return true; } /** * add elements to closure * * @param items * List */ public void addClosureElements(List items) { // do nothing } /** * make a closure * */ public void makeElementClosure(MethodConfiguration config) { // do nothing } public boolean showBrokenLinks() { return !options.isConvertBrokenLinks(); } /** * get the flag on whether to show extra descriptor info. If true, * information from linked element will be included in the descriptor page. * * @return boolean */ public boolean showExtraInfoForDescriptors() { return options.isShowMethodContentInDescriptors(); } /** * show descriptors on method element page */ public boolean showRelatedDescriptors() { return options.showRelatedDescriptors; } /** * get the tab id for the activity layout * * @return String */ public String getDefaultActivityTab() { return options.getDefaultActivityTab(); } @Override public boolean showLinkedPageForDescriptor() { return options.isShowLinkedPageForDescriptor(); } }