/*
* Copyright 2010 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.guvnor.tools.utils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.webdav.IResponse;
import org.guvnor.tools.Activator;
import org.guvnor.tools.Messages;
import org.guvnor.tools.utils.webdav.IWebDavClient;
import org.guvnor.tools.utils.webdav.ResourceProperties;
import org.guvnor.tools.utils.webdav.WebDavClientFactory;
import org.guvnor.tools.utils.webdav.WebDavException;
import org.guvnor.tools.utils.webdav.WebDavServerCache;
import org.guvnor.tools.views.model.ResourceHistoryEntry;
/**
* A set of utilities for dealing with (local) Guvnor metadata.
*/
public class GuvnorMetadataUtils {
private static final QualifiedName CONTROLLED_PROJECT_PROPERTY = new QualifiedName(Activator.PLUGIN_ID,"guvnorControlled");
/**
* Finds the local Guvnor metadata file associated with a given resource.
* @param resource The resource to locate metadata for.
* @return The metadata file for the given resource, null if metadata is not found.
*/
public static IFile findGuvnorMetadata(IResource resource) {
IFile res = null;
IPath dir = resource.getFullPath().removeLastSegments(1);
IPath mdpath = dir.append(".guvnorinfo").append("." + resource.getName()); //$NON-NLS-1$ //$NON-NLS-2$
IResource mdResource = resource.getWorkspace().getRoot().findMember(mdpath);
if (mdResource != null
&& mdResource.exists()
&& mdResource instanceof IFile) {
res = (IFile)mdResource;
}
return res;
}
/**
* Adds a resource to Guvnor.
* @param repLoc The location of the Guvnor repository
* @param targetLoc The location in Guvnor to add the resource
* @param selectedFile The resource to add
* @return true if the resource is added, false if there is already a resource
* with the same name in the given location.
*/
public static boolean addResourceToGuvnor(String repLoc,
String targetLoc,
IFile selectedFile) {
boolean res = false;
try {
String fullPath = targetLoc + selectedFile.getName();
IWebDavClient client = WebDavServerCache.getWebDavClient(repLoc);
if (client == null) {
client = WebDavClientFactory.createClient(new URL(repLoc));
WebDavServerCache.cacheWebDavClient(repLoc, client);
}
try {
// res = client.createResource(fullPath, selectedFile.getContents(), false);
// Hack: When creating a file, if the actual contents are passed first,
// the client hangs for about 20 seconds when closing the InputStream.
// Don't know why...
// But, if the file is created with empty contents, and then the contents
// set, the operation is fast (less than a couple of seconds)
res = client.createResource(fullPath, new ByteArrayInputStream(new byte[0]), false);
if (res) {
client.putResource(fullPath, selectedFile.getContents());
}
} catch (WebDavException wde) {
if (wde.getErrorCode() != IResponse.SC_UNAUTHORIZED) {
// If not an authentication failure, we don't know what to do with it
throw wde;
}
boolean retry = PlatformUtils.getInstance().
authenticateForServer(repLoc, client);
if (retry) {
// res = client.createResource(fullPath, selectedFile.getContents(), false);
// See Hack note immediately above...
res = client.createResource(fullPath, new ByteArrayInputStream(new byte[0]), false);
if (res) {
client.putResource(fullPath, selectedFile.getContents());
}
}
}
if (res) {
GuvnorMetadataUtils.markCurrentGuvnorResource(selectedFile);
ResourceProperties resProps = client.queryProperties(fullPath);
GuvnorMetadataProps mdProps =
new GuvnorMetadataProps(selectedFile.getName(),
repLoc,
fullPath, resProps.getLastModifiedDate(),
resProps.getRevision());
GuvnorMetadataUtils.setGuvnorMetadataProps(selectedFile.getFullPath(), mdProps);
}
} catch (Exception e) {
Activator.getDefault().displayError(IStatus.ERROR, e.getMessage(), e, true);
}
return res;
}
private static void setProjectPropertyForControlledResource(IFile selectedFile) throws Exception{
IProject project = selectedFile.getProject();
String value = project.getPersistentProperty(CONTROLLED_PROJECT_PROPERTY);
if(value == null){
project.setPersistentProperty(CONTROLLED_PROJECT_PROPERTY, "true");
}
}
/**
* Commits changes to Guvnor.
* @param selectedFile The Guvnor controlled file with pending changes
*/
public static void commitFileChanges(IFile selectedFile) {
try {
GuvnorMetadataProps props = GuvnorMetadataUtils.getGuvnorMetadata(selectedFile);
IWebDavClient client = WebDavServerCache.getWebDavClient(props.getRepository());
if (client == null) {
client = WebDavClientFactory.createClient(new URL(props.getRepository()));
WebDavServerCache.cacheWebDavClient(props.getRepository(), client);
}
ResourceProperties remoteProps = null;
try {
remoteProps = client.queryProperties(props.getFullpath());
} catch (WebDavException wde) {
if (wde.getErrorCode() != IResponse.SC_UNAUTHORIZED) {
// If not an authentication failure, we don't know what to do with it
throw wde;
}
boolean retry = PlatformUtils.getInstance().
authenticateForServer(props.getRepository(), client);
if (retry) {
remoteProps = client.queryProperties(props.getFullpath());
}
}
if (remoteProps == null) {
throw new Exception("Could not retrieve server version of " + props.getFullpath()); //$NON-NLS-1$
}
// Check to make sure that the version in the repository is the same as the base
// version for the local copy
boolean proceed = true;
if (!props.getRevision().equals(remoteProps.getRevision())) {
String msg = MessageFormat.format(Messages.getString("overwrite.confirmation"), //$NON-NLS-1$
new Object[] { selectedFile.getName(),
remoteProps.getRevision(),
props.getRevision() });
Display display = PlatformUI.getWorkbench().getDisplay();
proceed = MessageDialog.openQuestion(display.getActiveShell(),
Messages.getString("overwrite.confirmation.caption"), msg); //$NON-NLS-1$
}
if (proceed) {
client.putResource(props.getFullpath(), selectedFile.getContents());
GuvnorMetadataUtils.markCurrentGuvnorResource(selectedFile);
ResourceProperties resProps = client.queryProperties(props.getFullpath());
GuvnorMetadataProps mdProps = GuvnorMetadataUtils.getGuvnorMetadata(selectedFile);
mdProps.setVersion(resProps.getLastModifiedDate());
mdProps.setRevision(resProps.getRevision());
GuvnorMetadataUtils.setGuvnorMetadataProps(selectedFile.getFullPath(), mdProps);
}
} catch (Exception e) {
Activator.getDefault().displayError(IStatus.ERROR, e.getMessage(), e, true);
}
}
/**
* Finds the local Guvnor metadata file associated with a given resource.
* @param resource The resource path to locate metadata for.
* @return The metadata file for the given resource, null if metadata is not found.
*/
public static IFile findGuvnorMetadata(IPath resource) {
IFile res = null;
IPath dir = resource.removeLastSegments(1);
IPath mdpath = dir.append(".guvnorinfo").append("." + resource.lastSegment()); //$NON-NLS-1$ //$NON-NLS-2$
IResource mdResource = Activator.getDefault().getWorkspace().
getRoot().findMember(mdpath);
if (mdResource != null
&& mdResource.exists()
&& mdResource instanceof IFile) {
res = (IFile)mdResource;
}
return res;
}
public static boolean isGuvnorControlledResource(IResource resource) {
return findGuvnorMetadata(resource) != null;
}
public static GuvnorMetadataProps loadGuvnorMetadata(IFile mdFile) throws Exception {
setProjectPropertyForControlledResource(mdFile);
if(!mdFile.isTeamPrivateMember()){
mdFile.setTeamPrivateMember(true);
mdFile.setDerived(true);
if(mdFile.getParent().getName().endsWith(".guvnorinfo")){
mdFile.getParent().setTeamPrivateMember(true);
mdFile.getParent().setDerived(true);
}
}
Properties props = new Properties();
InputStream contents = mdFile.getContents();
try{
props.load(contents);
return new GuvnorMetadataProps(props.getProperty("filename"), //$NON-NLS-1$
props.getProperty("repository"), //$NON-NLS-1$
props.getProperty("fullpath"), //$NON-NLS-1$
props.getProperty("lastmodified"), //$NON-NLS-1$
props.getProperty("revision")); //$NON-NLS-1$
}
finally{
contents.close();
}
}
public static GuvnorMetadataProps getGuvnorMetadata(IResource resource) throws Exception {
IFile mdFile = findGuvnorMetadata(resource);
if (mdFile == null) {
return null;
}
return loadGuvnorMetadata(mdFile);
}
public static void writeGuvnorMetadataProps(File mdFile,
GuvnorMetadataProps mdProps) throws Exception {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mdFile);
Properties props = new Properties();
props.put("repository", mdProps.getRepository()); //$NON-NLS-1$
props.put("fullpath", mdProps.getFullpath()); //$NON-NLS-1$
props.put("filename", mdProps.getFilename()); //$NON-NLS-1$
props.put("lastmodified", mdProps.getVersion()); //$NON-NLS-1$
props.put("revision", mdProps.getRevision()); //$NON-NLS-1$
props.store(fos, null);
fos.flush();
} finally {
if (fos != null) {
fos.close();
}
}
}
public static void setGuvnorMetadataProps(IPath controlledFile,
GuvnorMetadataProps mdProps) throws Exception {
IWorkspaceRoot root = Activator.getDefault().getWorkspace().getRoot();
IFolder mdFolder = root.getFolder(
controlledFile.removeLastSegments(1).append(".guvnorinfo")); //$NON-NLS-1$
if (!mdFolder.exists()) {
mdFolder.create(true, true, null);
mdFolder.setTeamPrivateMember(true);
mdFolder.setDerived(true);
}
IFile mdFile = root.getFile(
mdFolder.getFullPath().append("." + controlledFile.lastSegment())); //$NON-NLS-1$
Properties props = new Properties();
if (!mdFile.exists()) {
mdFile.create(new ByteArrayInputStream(new byte[] {}), true, null);
mdFile.setTeamPrivateMember(true);
mdFile.setDerived(true);
} else {
props.load(mdFile.getContents());
}
if (mdProps.getRepository() != null) {
props.put("repository", mdProps.getRepository()); //$NON-NLS-1$
}
if (mdProps.getFullpath() != null) {
props.put("fullpath", mdProps.getFullpath()); //$NON-NLS-1$
}
if (mdProps.getFilename() != null) {
props.put("filename", mdProps.getFilename()); //$NON-NLS-1$
}
if (mdProps.getVersion() != null) {
props.put("lastmodified", mdProps.getVersion()); //$NON-NLS-1$
}
if (mdProps.getRevision() != null) {
props.put("revision", mdProps.getRevision()); //$NON-NLS-1$
}
OutputStream os = null;
try {
os = new FileOutputStream(new File(mdFile.getLocation().toOSString()));
props.store(os, null);
os.flush();
} finally {
if (os != null) {
os.close();
}
}
mdFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
}
public static IPath createGuvnorMetadataLocation(String rootPath) throws Exception {
IPath path = new Path(rootPath + File.separator + ".guvnorinfo"); //$NON-NLS-1$
if (!path.toFile().exists()) {
if (!path.toFile().mkdir()) {
throw new Exception("Could not create directory " + path.toOSString()); //$NON-NLS-1$
}
}
return path;
}
public static File getGuvnorMetadataFile(String path, String fname) {
return new File(path + File.separator + "." + fname); //$NON-NLS-1$
}
public static IFile getGuvnorControlledResource(IResource resource) throws Exception {
IFile res = null;
if (resource instanceof IFile) {
Properties props = new Properties();
props.load(((IFile)resource).getContents());
if (props.getProperty("filename") != null) { //$NON-NLS-1$
res = (IFile)Activator.getDefault().getWorkspace().
getRoot().findMember(resource.getFullPath().
removeLastSegments(2).append(props.getProperty("filename"))); //$NON-NLS-1$
}
}
return res;
}
public static boolean isGuvnorMetadata(IResource resource) {
return resource.getFullPath().removeLastSegments(1).
toOSString().endsWith(".guvnorinfo"); //$NON-NLS-1$
}
public static void markCurrentGuvnorResource(IResource resource) throws CoreException {
resource.setPersistentProperty(generateQualifiedName("version"), "current"); //$NON-NLS-1$ //$NON-NLS-2$
}
public static void markExpiredGuvnorResource(IResource resource) throws CoreException {
resource.setPersistentProperty(generateQualifiedName("version"), "expired"); //$NON-NLS-1$ //$NON-NLS-2$
}
public static String getGuvnorResourceProperty(IResource resource) throws CoreException {
return resource.getPersistentProperty(generateQualifiedName("version")); //$NON-NLS-1$
}
public static boolean isGuvnorResourceCurrent(IResource resource) throws CoreException {
String ver = resource.getPersistentProperty(generateQualifiedName("version")); //$NON-NLS-1$
if (ver == null) {
return false;
}
return ver.equals("current"); //$NON-NLS-1$
}
private static QualifiedName generateQualifiedName(String attr) {
return new QualifiedName("org.guvnor.tools", attr); //$NON-NLS-1$
}
public static ResourceHistoryEntry[] parseHistoryProperties(Properties entryProps) {
ResourceHistoryEntry[] entries = new ResourceHistoryEntry[entryProps.size()];
Enumeration<Object> en = entryProps.keys();
for (int i = 0; i < entryProps.size(); i++) {
String oneRevision = (String)en.nextElement();
String val = entryProps.getProperty(oneRevision);
StringTokenizer tokens = new StringTokenizer(val, ","); //$NON-NLS-1$
String verDate = null;
String author = null;
String comment = null;
try {
verDate = tokens.nextToken();
author = tokens.nextToken();
comment = tokens.nextToken();
} catch (NoSuchElementException e) {
// Don't care if some fields are missing
}
entries[i] = new ResourceHistoryEntry(oneRevision, verDate, author, comment);
}
return entries;
}
}