blob: 8f81ae85e7d164d381e5e17c1ad0bbe0343d72d0 [file] [log] [blame]
//------------------------------------------------------------------------------
// 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
protected List referencedElements = new ArrayList();
// published elements
protected 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() {
// 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();
}
}