Learn More About the Best Practices SharePoint Conference

Writing a Custom Data Provider for MashPoint

Blogs

    MashPoint - A Breakthrough in SharePoint Data Integration
  • Home
  • Contact

News Flash

Download MashPoint Now!

MashPoint - Data Integration for SharePointDownload the official MashPoint release, available as of November 7th, 2008.

Jonas Nilsson Q&A

In this section we will write a custom Data Provider for MashPoint.  This sounds much more complicated than it really is since all we are doing is writing a .Net class.  As a matter of fact, any .NET class can be imported into MashPoint.

Our sample provider will allow us to access the file system and display files from a file server in SharePoint.  Authentication and Authorization will be managed by MashPoint so no need to worry about this, and we can focus on the task of returning file information.

The only requirement for a class to be imported is that it's installed into the GAC on all web front ends (WFE's).  This implies that it has to be signed.

Let's start by bringing up Visual Studio, then create a ClassLibrary project, and name it MashPointProvider.

Create a new class called FileSystemProvider.

Here's our provider class:

   1: namespace MashPointProvider
   2: {
   3:     using System;
   4:     using System.IO;
   5:     using System.Collections.Generic;
   6:  
   7:     public class FileSystemProvider
   8:     {
   9:         public struct FileInformation
  10:         {
  11:             readonly FileInfo _fileInfo;
  12:             readonly DateTime _lastWriteTime;
  13:             readonly DateTime _created;
  14:             public FileInformation(FileInfo fileInfo)
  15:             {
  16:                 if (fileInfo == null)
  17:                 {
  18:                     throw new ArgumentNullException(); 
  19:                 }
  20:                 _fileInfo = fileInfo;
  21:                 _created = _fileInfo.CreationTime;
  22:                 _lastWriteTime = _fileInfo.LastWriteTime;
  23:             }
  24:  
  25:             public string Name
  26:             {
  27:                 get { return _fileInfo.Name; }
  28:             }
  29:             public DateTime LastWriteTime
  30:             {
  31:                 get { return _lastWriteTime; }
  32:             }
  33:             public string Path
  34:             {
  35:                 get { return _fileInfo.FullName; }
  36:             }
  37:             public DateTime Created
  38:             {
  39:                 get { return _created;  }
  40:             }
  41:             public Stream Stream
  42:             {
  43:                 get { return _fileInfo.OpenRead(); }
  44:             }
  45:         }
  46:
  47:         static bool AccessDirectory(DirectoryInfo di)
  48:         {
  49:             return (di.Attributes & FileAttributes.Directory)
  50:                 == FileAttributes.Directory;
  51:         }
  52:         private DirectoryInfo VerifyPathAndGetDirectoryInfo(string dirPath)
  53:         {
  54:             if (dirPath == null)
  55:             {
  56:                 throw new ArgumentNullException("dirPath");
  57:             }
  58:             if (dirPath == string.Empty)
  59:             {
  60:                 throw new ArgumentException("dirPath can't be empty");
  61:             }
  62:             DirectoryInfo di = new DirectoryInfo(dirPath);
  63:             // Access attributes property to generate AccessDenied 
  64:             // if the user does not have access to the folder
  65:             AccessDirectory(di);
  66:  
  67:             if (!di.Exists)
  68:             {
  69:                 string msg = String.Format("Directory [{0}] doesn't exist", dirPath);
  70:                 throw new ArgumentException(msg);
  71:             }
  72:             return di;
  73:         }
  74:         public FileInformation[] GetFiles(string dirPath)
  75:         {
  76:             DirectoryInfo di = VerifyPathAndGetDirectoryInfo(dirPath);
  77:             List<FileInformation> files = new List<FileInformation>();
  78:             foreach (FileInfo file in di.GetFiles())
  79:             {
  80:                 files.Add(new FileInformation(file));
  81:             }
  82:             return files.ToArray(); 
  83:         }
  84:     }
  85: }

Let's dissect this class because the hardest part is to generate the Application Definition File for it.  Let's hope that some of the popular BDC Editors (no names) will add support for generating ADF files from any .NET type.

To create the application definition file I suggest that you use the Amazon Web Service sample as a template.  The application definition file describes the types in a similar way as .NET meta data does.  So let's start with the LobSystem element.

Change the Type to Custom and Name to FileSystem.

   1: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
   2: <LobSystem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3:    xsi:schemaLocation="http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog BDCMetadata.XSD" 
   4:    Type="Custom" Version="1.0.0.0" Name="FileSystem" 
   5:    xmlns="http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog">

Now modify the Property elements.  A custom provider is a type, and a type resides in an assembly, so we need to tell MashPoint the fully qualified name to the type.  So the properties to add is CustomAssemblyFullName and ClassName.

   1: <Properties>
   2:   <Property Name="CustomAssemblyFullName" Type="System.String"> MashPointProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxx</Property>
   3:   <Property Name="ClassName" Type="System.String">MashPointProvider. FileSystemProvider</Property>
   4:   <Property Name="WildcardCharacter" Type="System.String">%</Property>
   5: </Properties>

Next we need to modify the LobSystemInstance element.  Add a property called CustomAuthenticationMode and set it to PassThrough. 

   1: <LobSystemInstances>
   2:     <LobSystemInstance Name="FileSystemInstance">
   3:       <Properties>
   4:         <Property Name="CustomAuthenticationMode" Type="System.String">PassThrough</Property>
   5:       </Properties>
   6:     </LobSystemInstance>
   7:   </LobSystemInstances>

Now it's time to define the entities.  We will have one entity named FileEntity.  The identifier of a file is its path so mark that as the Identifier

   1: <Entity Name="FileEntity">
   2:   <Identifiers>
   3:     <Identifier Name="Path" TypeName="System.String" />
   4:   </Identifiers>
   5:   <Methods/>
   6: </Entity> 

The next step is to "map" the method GetFiles.

   1: public FileInformation[] GetFiles(string dirPath)
   2: {
   3:     DirectoryInfo di = VerifyPathAndGetDirectoryInfo(dirPath);
   4:     List<FileInformation> files = new List<FileInformation>();
   5:     foreach (FileInfo file in di.GetFiles())
   6:     {
   7:         files.Add(new FileInformation(file));
   8:     }
   9:     return files.ToArray(); 
  10: }

 

It takes one input parameter string dirPath so let's describe this:

   1: <Method Name="GetFiles">
   2:  <Parameters>
   3:   <Parameter Direction="In" Name="DirPath">
   4:    <TypeDescriptor TypeName="System.String" Name="DirPath">
   5:     <DefaultValues>
   6:      <DefaultValue 
   7: MethodInstanceName="GetFilesFinderInstance" Type="System.String">[EnterThePathOfTheRootDirectory]</DefaultValue>
   8:      </DefaultValues>
   9:    </TypeDescriptor>
  10:   </Parameter> 

Now let's describe the return parameter.  It's a little bit more complex since we return an array of FileInformation structures.

Here's the public interface of the FileInformation structure.

   1: public struct FileInformation
   2: {
   3:     public string Name
   4:     {
   5:         get { return _fileInfo.Name; }
   6:     }
   7:     public DateTime LastWriteTime
   8:     {
   9:         get { return _lastWriteTime; }
  10:     }
  11:     public string Path
  12:     {
  13:         get { return _fileInfo.FullName; }
  14:     }
  15:     public DateTime Created
  16:     {
  17:         get { return _created;  }
  18:     }
  19:     public Stream Stream
  20:     {
  21:         get { return _fileInfo.OpenRead(); }
  22:     }
  23: }

Since the method returns an array, the first TypeDescriptor is marked with the IsCollection attribute.

   1: <Parameter Direction="Return"  Name="Response">
   2:  <TypeDescriptor TypeName="MashPointProvider.FileSystemProvider.FileInformation[], Bamboo.FileLister" IsCollection="true" Name="Files">
   3:   <TypeDescriptors>
   4:    <TypeDescriptor TypeName="MashPointProvider.FileSystemProvider.FileInformation, Bamboo.FileLister" Name="File">
   5:     <TypeDescriptors>
   6:      <TypeDescriptor TypeName="System.String" Name="Name"/>
   7:      <TypeDescriptor TypeName="System.String" Name="Path" IdentifierName="Path" AssociatedFilter="Paths" />
   8:      <TypeDescriptor TypeName="System.DateTime" Name="Created"/>
   9:      <TypeDescriptor TypeName="System.DateTime" Name="LastWriteTime"/>
  10:      </TypeDescriptor>
  11:     </TypeDescriptors>
  12:    </TypeDescriptor>
  13:   </TypeDescriptors>
  14:  </TypeDescriptor>
  15: </Parameter> 

Then we describe all the properties of the element in the array. You don't have to include all of them, just add the ones you want to expose.  In our example, we have left out the Stream property.  In a future post, we will show how to stream back binary data from MashPoint.

A method needs at least one method instance so let's define one.  A method instance is a method with a set of default values for the parameters.

   1: <MethodInstances>
   2:  <MethodInstance 
   3:     Name="GetFilesFinderInstance" 
   4:     Type="Finder" 
   5:     ReturnParameterName="Response" 
   6:     ReturnTypeDescriptorName="Files"/>
   7: </MethodInstances>

Now it's time to revisit the input parameter that we described in the beginning.  We just said that a method instance is a reference to a method and a set of default parameters.  This is where the DefaultValue element comes into play.

You can define default values for methods to make it easy for the client to use them.  We will specify the default directory that will be enumerated if the client does not specify a value. In this example, we will expose c:\Windows

   1: <Parameter Direction="In"  Name="DirPath">
   2:  <TypeDescriptor TypeName="System.String" Name="DirPath">
   3:   <DefaultValues>
   4:    <DefaultValue 
   5:       MethodInstanceName="GetFilesFinderInstance" Type="System.String">C:\Windows\</DefaultValue>
   6:    </DefaultValues>
   7:   </TypeDescriptor>
   8:  </Parameter>

Note that you can have multiple default values for different method instances.

Let's put this all together to see how the application definition file looks.  Don't forget to update the public key of your assembly before trying to load it into MashPoint

   1: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
   2: <LobSystem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog BDCMetadata.XSD" Type="Custom" Version="1.0.0.0" Name="FileLister" xmlns="http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog">
   3:  <Properties>
   4:   <Property Name="CustomAssemblyFullName" Type="System.String">MashPointProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226a0f1c8f42bfc</Property>
   5:   <Property Name="ClassName" Type="System.String">MashPointProvider.FileSystemProvider</Property>
   6:   <Property Name="WildcardCharacter" Type="System.String">%</Property>
   7:  </Properties>
   8:  <LobSystemInstances>
   9:   <LobSystemInstance Name="FileListerInstance">
  10:    <Properties>
  11:     <Property Name="CustomAuthenticationMode" Type="System.String">PassThrough</Property>
  12:    </Properties>
  13:   </LobSystemInstance>
  14:  </LobSystemInstances>
  15:  <Entities>
  16:  
  17:  <Entity Name="FileEntity">
  18:   <Identifiers>
  19:    <Identifier Name="Path" TypeName="System.String" />
  20:   </Identifiers>
  21:   <Methods>
  22:    <Method Name="GetFiles">
  23:     <Parameters>
  24:      <Parameter Direction="In"  Name="DirPath">
  25:       <TypeDescriptor TypeName="System.String" Name="DirPath">
  26:        <DefaultValues>
  27:         <DefaultValue MethodInstanceName="GetFilesFinderInstance" Type="System.String">c:\windows</DefaultValue>
  28:        </DefaultValues>
  29:       </TypeDescriptor>
  30:      </Parameter>
  31:      <Parameter Direction="Return"  Name="Response">
  32:       <TypeDescriptor 
  33: TypeName="MashPointProvider.FileSystemProvider.FileInformation[], Bamboo.FileLister" IsCollection="true" Name="Files">
  34:        <TypeDescriptors>
  35:         <TypeDescriptor 
  36: TypeName="MashPointProvider.FileSystemProvider.FileInformation, Bamboo.FileLister" Name="File">
  37:          <TypeDescriptors>
  38:       <TypeDescriptor TypeName="System.String" Name="Name"/>
  39:       <TypeDescriptor TypeName="System.String" Name="Path" IdentifierName="Path"/>
  40:       <TypeDescriptor TypeName="System.DateTime" Name="Created"/>
  41:   <TypeDescriptor TypeName="System.DateTime" Name="LastWriteTime"/>
  42:          </TypeDescriptors>
  43:         </TypeDescriptor>
  44:        </TypeDescriptors>
  45:       </TypeDescriptor>
  46:      </Parameter>
  47:     </Parameters>
  48:     <MethodInstances>
  49:      <MethodInstance 
  50: Name="GetFilesFinderInstance" 
  51: Type="Finder" 
  52: ReturnParameterName="Response" 
  53: ReturnTypeDescriptorName="Files"/>
  54:     </MethodInstances>
  55:    </Method>
  56:   </Methods>
  57:  </Entity>
  58: </Entities>
  59: </LobSystem>

Now it's time to import the file into MashPoint.  Once uploaded, you should see a page like this:

Click on the FileEntity to see the details.

Now let's connect the Bamboo Data-Viewer web part and see if we can get some file listings back.

This should give you an idea of how easy it is to write a custom provider for MashPoint.  You don't have to worry about authentication and authorization.  Once you have created a custom provider, it will work with all MashPoint-enabled Web Parts from Bamboo Solutions and other vendors.

In the next section, we will describe how to write code that uses the MashPoint runtime object model.

/Jonas


Posted Jun 06 2008, 04:48 PM by John Anderson

Comments

The Bamboo Team Blog wrote Announcing the Launch of MashPoint - A Free Data Integration Platform for SharePoint
on Fri, Nov 7 2008 8:26 PM

Today, Bamboo Solutions has launched MashPoint , a new, free data integration platform for SharePoint

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
  
Bamboo Solutions Corporation, 2002-2008