First, yes I'm back, after a couple years out, I decided to reactivate this blog and now I have tons of things to talk about.
For my "Welcome back" article I'll do a very nice peace of code. The demand has started with a customer asking for a solution to read a "pom.xml" file from an Stream, increment the version number attribute and deliver it back to the stream. So I began my research and found an article from Kelvin that did help me a lot: Reading and writing files directly on a stream. And of course I found this article by the Ralph's blog tsjazz | Jazz in Flight . (which is the best blog about RTC API that I know of).
That article showed me the way, but I still had a problem, the article was all about using client API and as I said, the customer needed to do this at server side, more specifically using an IOperationParticipant. So, after some hours of digging the RTC SDK, I was able to get it done. So, here is it.
First, the method to update the file
/** * Update the content of a file * * @param workItem * @param filePath * @param stream * @param component * @param myService * @return */ public boolean updatePomVersion(IWorkItem workItem, String filePath, IWorkspace stream, IComponent component, AbstractService myService) { // File pat, e.g. src/pom.xml IPath pomPath = new Path(filePath); // Getting services IScmItemService scmItemService = (IScmItemService) myService.getService(IScmItemService.class); IScmService scmService = (IScmService) myService.getService(IScmService.class); ServiceConfigurationProvider config = ServiceConfigurationProvider.FACTORY.create(stream, component); IServerSideVersionedContentService contentService = myService.getService(IServerSideVersionedContentService.class); IServerSideFileContentService fileContentService = myService.getService(IServerSideFileContentService.class); // Trying to locate the file within the component IVersionableHandle versionableHandle; try { versionableHandle = scmService.configurationResolvePath( config, component.getRootFolder(), pomPath.segments(), null, null); // File not found if (versionableHandle == null) { System.out.println("Versionable not found."); return false; } else { // Fetching Versionable from its handle IVersionable versionable = scmItemService.fetchState(versionableHandle, IScmItemService.COMPLETE); // If not file (that is just to be safe) if( !(versionable instanceof IFileItem ) ) { System.out.println("Versionable is not a file"); //$NON-NLS-1$ return false; } else { // Reading file content IFileItem file = (IFileItem) versionable; IFileContent content = file.getContent(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); contentService.fetchContentTrusted(versionableHandle, content.getHash(), outStream); // Now that you have the content of the file in "outStream" object // I'll skip the code to update the file content // as this is not the goal of this article. // Let's just assume that you have the new // content in a new object called "newOutputStream" // with something like: // ByteArrayOutputStream newOutputStream = updateStreamContent(outStream); // Updating File content IFileItem fileWorkingCopy = (IFileItem) file.getWorkingCopy(); IFileContent newFileContent = createFileContent(newOutputStream.toByteArray(), fileContentService); ICommitParameter param = ICommitParameter.FACTORY.create(); fileWorkingCopy.setContent((IFileContent) newFileContent); param.addItemToSave(fileWorkingCopy); // Applying lock and delivering applyLock(stream, component, fileWorkingCopy, scmService); try { // Create change set IChangeSetHandle cs = scmService.createChangeSetForStream(stream, component, "Changeset Comment - File content updated", param, IScmService.DELTA_PER_INVOCATION, null); IChangeSetHandle[] csHandles = new IChangeSetHandle[1]; csHandles[0] = cs; // Deliver scmService.deliverCombined(null, null, stream, new IBaselineHandle[0], csHandles, null, null, null, null); // I'll cover how to associate a changeset with an work item (server side) in another post // So this is only to get you curious :) // associateWorkItem(workItem, cs, stream); return true; } finally { // Remove the lock removeLock(stream, component, fileWorkingCopy, scmService); } } } } catch (Exception e) { return false; } }
Now let's look at the others methods created.
Starting with the two methods to lock and unlock the file. This is necessary to create a changeset.
@SuppressWarnings("unchecked") /** * Lock the file * * @param streamHandle * @param componentHandle * @param fileItem * @param scmService * @throws TeamRepositoryException */ private void applyLock(IWorkspace streamHandle, IComponentHandle componentHandle, IFileItem fileItem, IScmService scmService) throws TeamRepositoryException { // Commit parameter ICommitParameter instruction = ICommitParameter.FACTORY.create(); instruction.addItemToSave(fileItem); // Setting up workspace/stream, component and contributor LockParameter operationParameter = ScmDtoFactory.eINSTANCE.createLockParameter(); WorkspaceLocks wl = ScmDtoFactory.eINSTANCE.createWorkspaceLocks(); operationParameter.getToAcquire().add(wl); wl.setWorkspace(streamHandle); ComponentLocks cl = ScmDtoFactory.eINSTANCE.createComponentLocks(); cl.setComponent(componentHandle); wl.getComponentLocks().add(cl); ContributorLocks contL = ScmDtoFactory.eINSTANCE.createContributorLocks(); contL.setContributor(getAuthenticatedContributor()); contL.getVersionables().add(fileItem); cl.getContributorLocks().add(contL); // Locking file scmService.updateLocks(operationParameter , null); } @SuppressWarnings("unchecked") /** * * @param streamHandle * @param componentHandle * @param fileItem * @param scmService * @throws TeamRepositoryException */ private void removeLock(IWorkspace streamHandle, IComponentHandle componentHandle, IFileItem fileItem, IScmService scmService) throws TeamRepositoryException { // Commit parameter ICommitParameter instruction = ICommitParameter.FACTORY.create(); instruction.addItemToSave(fileItem); // Setting up workspace/stream, component and contributor LockParameter operationParameter = ScmDtoFactory.eINSTANCE.createLockParameter(); WorkspaceLocks wl = ScmDtoFactory.eINSTANCE.createWorkspaceLocks(); operationParameter.getToRelease().add(wl); wl.setWorkspace(streamHandle); ComponentLocks cl = ScmDtoFactory.eINSTANCE.createComponentLocks(); cl.setComponent(componentHandle); wl.getComponentLocks().add(cl); ContributorLocks contL = ScmDtoFactory.eINSTANCE.createContributorLocks(); contL.setContributor(getAuthenticatedContributor()); contL.getVersionables().add(fileItem); cl.getContributorLocks().add(contL); // Removing the lock scmService.updateLocks(operationParameter , null); }
And lastly the method to create the file content
@SuppressWarnings("restriction") /** * Create an IFileContent with the bytes informed * @param bytes Usually coming from an Input or Output Stream * @param fileContentService Service used to store de content * @return * @throws TeamRepositoryException */ private IFileContent createFileContent(byte[] bytes, IServerSideFileContentService fileContentService) throws TeamRepositoryException { // Generate has code ContentHash contentHashCode; try { contentHashCode = ContentHash.valueOf(new UnsynchronizedByteArrayInputStream(bytes)); } catch (IOException e) { throw new TeamRepositoryException(e); } // Create an Input Stream with the informed bytes ByteArrayInputStream inStream = new ByteArrayInputStream(bytes); // Store the content fileContentService.storeContent(contentHashCode, bytes.length, null, IFileContent.ENCODING_UTF_8, FileLineDelimiter.LINE_DELIMITER_NONE, 0, inStream); // Setting the file content object FileContent c = FilesystemFactory.eINSTANCE.createFileContent(); c.setHash(contentHashCode); c.setPredecessorHint((ContentHash)null); c.setSize(bytes.length); c.setCharacterEncoding(IFileContent.ENCODING_UTF_8); c.setLineDelimiter(FileLineDelimiter.LINE_DELIMITER_NONE); c.setLineDelimiterCount(0); return c; }
All the code above runs directly with in a IOperationParticipant. You'll need the follow interfaces to make it works:
<requiredservice interface="com.ibm.team.repository.service.IRepositoryItemService"> <requiredservice interface="com.ibm.team.repository.service.IServerQueryService"> <requiredservice interface="com.ibm.team.repository.service.IContentService"> <requiredservice interface="com.ibm.team.scm.common.IScmService"> <requiredservice interface="com.ibm.team.scm.service.IScmItemService"> <requiredservice interface="com.ibm.team.scm.service.IServerSideVersionedContentService"> <requiredservice interface="com.ibm.team.repository.common.service.IPermissionService"> <requiredservice interface="com.ibm.team.repository.common.service.IContributorService"> <requiredservice interface="com.ibm.team.filesystem.service.internal.IServerSideFileContentService"> <requiredservice interface="com.ibm.team.process.service.IProcessServerService"> <requiredservice interface="com.ibm.team.repository.service.ITransactionService">
That is it! Let me know if you need anything else.