понеділок, 16 листопада 2009 р.

Java to Sharepoint integration. (Copy files solution)

So, this is my first publication :)  Last week i had a task to implement sharepoint integration lib and it was not so trivial task as i thought. But.. task completed and after hours of investigation i`d like to share my knowledge about HOW TO COPY FILES AND CREATE FOLDER FROM JAVA APPLICATION TO SHAREPOINT.



1) Let`s create new Maven project and add dependencies:

<dependencies>
   <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.2</version>
   <scope>test</scope>
   </dependency>
   <dependency>
   <groupId>wsdl4j</groupId>
   <artifactId>wsdl4j</artifactId>
   <version>1.6.1</version>
   <scope>runtime</scope>
   </dependency>
   <dependency>
   <groupId>javax.activation</groupId>
   <artifactId>activation</artifactId>
   <version>1.0.2</version>
   </dependency>
   <dependency>
   <groupId>javax.mail</groupId>
   <artifactId>mail</artifactId>
   <version>1.3.2</version>
   </dependency>
   <dependency>
   <groupId>org.apache.axis</groupId>
   <artifactId>axis</artifactId>
   <version>1.4</version>
   </dependency>
   <dependency>
   <groupId>axis</groupId>
   <artifactId>axis-jaxrpc</artifactId>
   <version>1.4</version>
   </dependency>
   <dependency>
   <groupId>commons-logging</groupId>
   <artifactId>commons-logging</artifactId>
   <version>1.1.1</version>
   </dependency>  
   <dependency>
   <groupId>commons-discovery</groupId>
   <artifactId>commons-discovery</artifactId>
   <version>0.4</version>
   <scope>test</scope>
   </dependency>
  </dependencies>


2) For copying files we`ll use sharepoint`s web services :Copy and for creating new folders : Lists. This services situated on http://yoursharepointurl/_vti_bin/Copy.asmx and http://yoursharepointurl/_vti_bin/Lists.asmx
I found a maven plugin which generate all java classes from wsdl file.

<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>axistools-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
<configuration>
<outputDirectory>${project.build.directory}/${project.artifactId}/generated-sources</outputDirectory>
<timestampDirectory>${project.build.directory}/${project.artifactId}/generated-sources</timestampDirectory>
<testSourceDirectory>${project.build.directory}/${project.artifactId}/generated-test-sources</testSourceDirectory>
<serverSide>false</serverSide>
<subPackageByFileName>false</subPackageByFileName>
<typeMappingVersion>1.1</typeMappingVersion>
<testCases>false</testCases>
<runTestCasesAsUnitTests>false</runTestCasesAsUnitTests>
</configuration>
</plugin>
</plugins>


3) Save your wsdl files to src/main/wsdl folder and run maven goal generate-sources. Plugin will generate all java classes. Add these files to build path to Eclipse.



4) Now time is to create SharepointService.java class. Add constructor

public SharePointService(String serviceUrl, String userPassword, String userName) {
this.servicesUrl = serviceUrl;
this.userName = userName;
this.userPassword = userPassword;
this.listsService = initListService();
this.copyService = initCopyService();
}
5) add private methods to initialise listsService and copyService. In case if you have NTLM authentication you should look here : http://derekjmiller.spaces.live.com/blog/cns!A77124B9D0EE317F!142.entry

public CopySoap12Stub initCopyService() {
CopySoap12Stub stub;
CopyLocator locator = new CopyLocator();
try {
URL url = new URL(this.servicesUrl + COPY_SERVICE);
stub = (CopySoap12Stub) locator.getCopySoap12(url);
stub.setUsername(this.userName);
stub.setPassword(this.userPassword);
return stub;
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
}
return null;
}

and for lists service :
private ListsSoap12Stub initListService() {
if (this.listsService != null)
return this.listsService;

ListsSoap12Stub stub;
ListsLocator locator = new ListsLocator();
try {
URL url = new URL(this.servicesUrl + LISTS_SERVICE);
stub = (ListsSoap12Stub) locator.getListsSoap12(url);
stub.setUsername(userName);
stub.setPassword(userPassword);
return stub;
} catch (MalformedURLException e) {
logger.error(e.getMessage());
} catch (ServiceException e) {
logger.error(e.getMessage());
} catch (Exception ex) {
logger.error(ex.getMessage());
}
return null;
}
6) I added rootPath property - this is the document library on  where folders and file will be uploaded

private String rootPath;


public void setRootPath(String path) {
this.rootPath = path;
}

public String getRootPath() {
if (this.rootPath == null) {
return DEFAULT_ROOT_FOLDER;
} else
return this.rootPath;
}


7) Interface of our upload-method will be :  uploadDocument(String path, File file) where path - path where to upload file, relative to the RootPath  and file - simple java.io.File.
Bad news is thst copy service CANT create folders, just copy file. Good news - we can to this with Lists service.
Here the implementation:


private void makeNewFolder(String relativePath) throws SOAPException,
RemoteException {
UpdateListItemsUpdates updates;
String[] folderNames = relativePath.split("/");
if (folderNames.length == 0) {
logger
.info("Path should have delimiters: example: ParentFolder/ChildFolder");
return;
} else {
String currentFolder = folderNames[0];
for (int i = 0; i < folderNames.length; i++) {

if (i > 0) {
currentFolder += "/" + folderNames[i];
}
updates = new UpdateListItemsUpdates(BatchCreator
.createNewFolderBatch(currentFolder));
listsService.updateListItems(DEFAULT_ROOT_FOLDER, updates);
}
}
}


The main thing here is - updateListItems method . We need to pass 2 parameters there :
DEFAULT_ROOT_FOLDER - name of the list (name of the shared library, which we will update)
updates - Wrapper of MessageElement[] . We create it in our utill class BatchCreator in createNewFolderBatch method:

8) Ant the last - put it all together implementing uploadDocument(String path, File file) method:


public void uploadDocument(String path, File file)
throws FileAlreadyExistsException, SOAPException {
try {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(file));
byte[] bytes = new byte[bis.available()];
bis.read(bytes);
FieldInformation fieldInformation = new FieldInformation();
fieldInformation.setDisplayName(file.getName());
fieldInformation.setInternalName(file.getName());
FieldInformationCollection infos = new FieldInformationCollection(
new FieldInformation[] { fieldInformation });

makeNewFolder(path);

String sourceUrl = getServiceUrl() + "/" + getRootPath() + "/"
+ path + "/" + file.getName();
DestinationUrlCollection destinationUrls = new DestinationUrlCollection(
new String[] { sourceUrl });

UnsignedIntHolder copyIntoItemsResult = new UnsignedIntHolder(
new UnsignedInt());
CopyResultCollectionHolder results = new CopyResultCollectionHolder(
new CopyResultCollection(
new CopyResult[] { new CopyResult() }));

copyService.copyIntoItems(sourceUrl, destinationUrls, infos, bytes,
copyIntoItemsResult, results);
// error message always exists
String errorMessage = results.value.getCopyResult()[0]
.getErrorMessage();
if (errorMessage != null) {
throw new FileAlreadyExistsException("File already exists in directory");
} else {
logger.info("file " + file.getName()
+ " has been successfully uploaded to server" + "\n "
+ getServiceUrl() + "/" + getRootPath() + "/" + path
+ "/" + file.getName());
}

} catch (FileNotFoundException e) {
logger.error(e.getMessage());
} catch (IOException e) {
logger.error(e.getMessage());
}
}



This works for me and i hope will be work for you. ;)

3 коментарі:

  1. This is very impressive. I'm having a few issues with compilation (such as this signature not being found:

    FieldInformationCollection infos = new FieldInformationCollection( new FieldInformation[] { fieldInformation });

    Do you have the ability to post a zip file of this solution?

    Thanks a lot.

    ВідповістиВидалити
  2. unfortunately i`ve lost all sources, but as i remember FieldInformationCollection class should be generated by wsdl2java from sharepoint COPY-webservice http://msdn.microsoft.com/en-us/library/dd920449.aspx

    ВідповістиВидалити
  3. Hi, Thanks for such informative post. I am trying to create a folder in sahrepoint. while trying that I got stuck on this line.

    updates = new UpdateListItemsUpdates(BatchCreator
    .createNewFolderBatch(currentFolder));

    I didnt understand where did you get UpdateListItemsUpdates class from. Your post doesnot tell about this class and neither I could find it in Lists.asmx

    Please let how to pass the MessageElement[] in UpdateListItems()

    Thanks again

    ВідповістиВидалити