| /******************************************************************************* |
| * Copyright (c) 2010, 2012 Ericsson |
| * |
| * 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 |
| * |
| * Description: |
| * |
| * Contributors: |
| * Alvaro Sanchez-Leon - First API and implementation |
| *******************************************************************************/ |
| /** |
| * |
| */ |
| package org.eclipse.mylyn.reviews.r4e.core.model.serial.impl; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.mylyn.reviews.r4e.core.Activator; |
| import org.eclipse.mylyn.reviews.r4e.core.model.R4EParticipant; |
| import org.eclipse.mylyn.reviews.r4e.core.model.serial.IModelWriter; |
| import org.eclipse.mylyn.reviews.r4e.core.model.serial.Persistence; |
| import org.eclipse.mylyn.reviews.r4e.core.utils.filePermission.FileSupportCommandFactory; |
| |
| /** |
| * @author Alvaro Sanchez-Leon |
| * |
| * @version $Revision: 1.0 $ |
| */ |
| public class ChangeResController implements Persistence.ResourceUpdater { |
| // ------------------------------------------------------------------------ |
| // Constants |
| // ------------------------------------------------------------------------ |
| private static final String LOCK_EXT = ".lck"; |
| protected static final Long INACTIVE_BOOKING = -1L; |
| // ------------------------------------------------------------------------ |
| // Fields |
| // ------------------------------------------------------------------------ |
| /** |
| * Field checkedOutMap. |
| */ |
| protected final Map<Long, UpdateContext> checkedOutMap = new HashMap<Long, UpdateContext>(); |
| /** |
| * Field fcount. |
| */ |
| protected Long fcount = 0L; |
| // private R4EReader fReader = SerializeFactory.getReader(); |
| /** |
| * Field fWriter. |
| */ |
| protected IModelWriter fWriter = SerializeFactory.getWriter(); |
| |
| protected Persistence.IResSerializationState fResState = null; |
| |
| // ------------------------------------------------------------------------ |
| // Constructors |
| // ------------------------------------------------------------------------ |
| public ChangeResController(Persistence.IResSerializationState aResState) { |
| fResState = aResState; |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Methods |
| // ------------------------------------------------------------------------ |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.mylyn.reviews.r4e.core.model.serial.Persistence.ResourceUpdater#checkOut(org.eclipse.emf.ecore.EObject |
| * , org.eclipse.mylyn.reviews.r4e.core.model.R4EParticipant) |
| */ |
| /** |
| * Method checkOut. |
| * @param aEObject EObject |
| * @param participant R4EParticipant |
| * @return Long |
| * @throws ResourceHandlingException |
| * @see org.eclipse.mylyn.reviews.r4e.core.model.serial.Persistence$ResourceUpdater#checkOut(EObject, R4EParticipant) |
| */ |
| public Long checkOut(EObject aEObject, String usrLoginID) throws ResourceHandlingException { |
| //Validate |
| if (aEObject == null || aEObject.eResource() == null || aEObject.eResource().getURI() == null) { |
| StringBuilder sb = new StringBuilder("Unable to update/save Element"); |
| if (aEObject == null) { |
| sb.append(", Element is null"); |
| } else if (aEObject.eResource() == null) { |
| sb.append(", The Resource associated to the Element is null, Element: " + aEObject.toString()); |
| } else if (aEObject.eResource().getURI() == null) { |
| sb.append(", The URI associated to the Element's Resource is null, Element: " + aEObject.toString()); |
| } |
| |
| throw new ResourceHandlingException(sb.toString()); |
| } |
| |
| if (fResState.isSerializationInactive(aEObject.eResource())) { |
| return INACTIVE_BOOKING; |
| } |
| |
| UpdateContext newContext = new UpdateContext(aEObject, usrLoginID); |
| // if resource is already checkedout, return existing booking number |
| Collection<UpdateContext> checkedOutContextList = checkedOutMap.values(); |
| for (Iterator<UpdateContext> iterator = checkedOutContextList.iterator(); iterator.hasNext();) { |
| UpdateContext updateContext = iterator.next(); |
| if (updateContext.equals(newContext)) { |
| // already checkedout |
| return updateContext.getBookingNum(); |
| } |
| } |
| |
| // TODO: Poll for directory changes, if directory is out of synch report it to UI via a new OutOfSynchException |
| |
| // if new checkout, |
| lockResource(aEObject.eResource(), usrLoginID); |
| |
| // get new booking number and register checkout |
| fcount++; |
| newContext.setBookingNum(fcount); |
| checkedOutMap.put(fcount, newContext); |
| |
| Activator.fTracer.traceDebug("Checkout Num: " + fcount + ", Object: " + aEObject.toString()); |
| |
| // return booking number |
| return fcount; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.mylyn.reviews.r4e.core.model.serial.Persistence.ResourceUpdater#undoCheckOut(java.lang.Long) |
| */ |
| /** |
| * Method undoCheckOut. |
| * |
| * @param aBookingNumber |
| * Long |
| * @throws ResourceHandlingException |
| * @see org.eclipse.mylyn.reviews.r4e.core.model.serial.Persistence$ResourceUpdater#undoCheckOut(Long) |
| */ |
| public void undoCheckOut(Long aBookingNumber) throws ResourceHandlingException { |
| if (aBookingNumber.equals(INACTIVE_BOOKING)) { |
| // Checked-out while resource serialisation was inactive i.e.nothing to do |
| return; |
| } |
| // Check if part of booking list |
| UpdateContext context = checkedOutMap.remove(aBookingNumber); |
| // if present on record |
| if (context != null) { |
| unlockResource(context.getResource()); |
| } else { |
| Activator.fTracer.traceError(new StringBuilder("UndoCheckOut failed: booking number is not active: " |
| + aBookingNumber).toString()); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.mylyn.reviews.r4e.core.model.serial.Persistence.ResourceUpdater#checkIn(java.lang.Long) |
| */ |
| /** |
| * Method checkIn. |
| * @param aBookingNumber Long |
| * @throws ResourceHandlingException |
| * @see org.eclipse.mylyn.reviews.r4e.core.model.serial.Persistence$ResourceUpdater#checkIn(Long) |
| */ |
| public void checkIn(Long aBookingNumber) throws ResourceHandlingException { |
| if (aBookingNumber == null) { |
| Activator.fTracer.traceDebug("CheckIn with booking number set to null"); |
| return; |
| } |
| |
| if (aBookingNumber.equals(INACTIVE_BOOKING)) { |
| // Checked-out while resource serialisation was inactive i.e.nothing to do |
| return; |
| } |
| |
| UpdateContext context = checkedOutMap.remove(aBookingNumber); |
| // Check if booking still on records |
| if (context != null) { |
| // if on record SAVE resource |
| Resource resource = context.getResource(); |
| fWriter.saveResource(resource); |
| // remove lock |
| unlockResource(resource); |
| Activator.fTracer.traceDebug("CheckIn: " + aBookingNumber); |
| } else { |
| Activator.fTracer.traceError(new StringBuilder("CheckIn failed: booking number is not active: " |
| + aBookingNumber).toString()); |
| } |
| } |
| |
| /** |
| * @param aResource |
| * @return |
| * @throws ResourceHandlingException |
| */ |
| protected void lockResource(Resource aResource, String usrLoginID) throws ResourceHandlingException { |
| if (aResource == null || aResource.getURI() == null) { |
| return; |
| } |
| |
| URI resUri = aResource.getURI(); |
| File file = new File(URI.decode(resUri.devicePath())); |
| |
| if (!file.exists()) { |
| throw new ResourceHandlingException("Not able to lock file: " + file.getAbsolutePath() |
| + ". file does not exist"); |
| } |
| |
| file = new File(resUri.toFileString() + LOCK_EXT); |
| // check if the file exist and if what is the user locking it |
| if (file.exists()) { |
| // The resource is already locked |
| try { |
| // TODO: Preference. |
| Thread.sleep(5000); |
| } catch (InterruptedException e) { |
| Activator.fTracer.traceDebug(new StringBuilder("Interrupter exception while waiting to lock resource") |
| .toString()); |
| } |
| |
| // Try a second time, if lock file does not longer exists continue |
| if (file.exists()) { |
| FileReader reader; |
| try { |
| reader = new FileReader(file); |
| BufferedReader breader = new BufferedReader(reader); |
| StringBuilder sb = new StringBuilder(); |
| try { |
| String line = breader.readLine(); |
| while (line != null) { |
| sb.append(line + "\n"); |
| line = breader.readLine(); |
| } |
| } catch (IOException e) { |
| Activator.fTracer |
| .traceDebug(new StringBuilder("IOException while reading lock file").toString()); |
| } |
| finally { |
| if (breader != null) { |
| try { |
| breader.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| throw new ResourceHandlingException("The Resource is locked: " + file.getAbsolutePath() |
| + "\nLock Information:\n" + sb.toString()); |
| } catch (FileNotFoundException e) { |
| // Quite unlikely but |
| // file is now gone, assume good timing and proceed |
| } |
| } |
| } |
| |
| // create the lock file |
| try { |
| file.createNewFile(); |
| // TODO: Preference. grant it write permissions to everyone should be an option |
| FileSupportCommandFactory.getInstance().grantWritePermission(file.getAbsolutePath()); |
| } catch (IOException e) { |
| throw new ResourceHandlingException(e); |
| } |
| |
| // Write lock tracking info |
| PrintWriter writer = null; |
| try { |
| writer = new PrintWriter(file); |
| writer.println("user: " + usrLoginID); |
| writer.println("Date: " + new Date().toString()); |
| |
| } catch (FileNotFoundException e) { |
| throw new ResourceHandlingException(e); |
| } finally { |
| writer.close(); |
| } |
| |
| Activator.fTracer.traceDebug("Checkout lock created " + file.getAbsolutePath()); |
| } |
| |
| // /** |
| // * @param aResource |
| // |
| // * @throws ResourceHandlingException */ |
| // protected void reloadResource(EObject aEObject) throws ResourceHandlingException { |
| // if (aEObject instanceof R4EReviewGroup) { |
| // // if resource is group - simple resource reload |
| // Resource aResource = aEObject.eResource(); |
| // fReader.reloadResource(aResource); |
| // return; |
| // } |
| // |
| // RModelFactoryExt resFactory = SerializeFactory.getModelExtension(); |
| // |
| // // if review object, close and re-open the review, to build new references |
| // if (aEObject instanceof R4EReview) { |
| // R4EReview review = (R4EReview) aEObject; |
| // String reviewName = review.getName(); |
| // R4EReviewGroup group = (R4EReviewGroup) review.eContainer(); |
| // resFactory.closeR4EReview((R4EReview) aEObject); |
| // resFactory.openR4EReview(group, reviewName); |
| // } |
| // |
| // |
| // URI resUri = aResource.getURI(); |
| // ResourceSet resSet = aResource.getResourceSet(); |
| // Resource reviewResource = null; |
| // EList<Resource> resources = resSet.getResources(); |
| // for (Iterator<Resource> iterator = resources.iterator(); iterator.hasNext();) { |
| // Resource resource = iterator.next(); |
| // resUri = resource.getURI(); |
| // if (fReader.isReviewResourceUri(resUri)) { |
| // reviewResource = resource; |
| // break; |
| // } |
| // } |
| // if (reviewResource != null) { |
| // TreeIterator<EObject> iterator = EcoreUtil.getAllContents(reviewResource, false); |
| // } |
| // |
| // } |
| |
| /** |
| * @param resource |
| * @throws ResourceHandlingException |
| */ |
| protected void unlockResource(Resource aResource) throws ResourceHandlingException { |
| if (aResource == null || aResource.getURI() == null) { |
| return; |
| } |
| |
| URI resUri = aResource.getURI(); |
| File file = new File(URI.decode(resUri.devicePath())); |
| if (!file.exists()) { |
| throw new ResourceHandlingException("Not able to unlock Resource file: " + file.getAbsolutePath() |
| + ". File does not exist"); |
| } |
| |
| String fileStr = resUri.toFileString() + LOCK_EXT; |
| file = new File(fileStr); |
| // check if the file to delete exists |
| if (file.exists()) { |
| // remove the file |
| file.delete(); |
| Activator.fTracer.traceDebug("CheckIn, Lock file removed: " + fileStr); |
| } else { |
| StringBuilder message = new StringBuilder("Lock to remove does not exist"); |
| String absPath = file.getAbsolutePath(); |
| if (absPath != null) { |
| message.append(": " + absPath); |
| } |
| Activator.fTracer.traceError(message.toString()); |
| } |
| } |
| |
| /** |
| */ |
| static class UpdateContext { |
| private String fUser; |
| private EObject fEObject; |
| private Long fBookingNum = null; |
| |
| /** |
| * Constructor for UpdateContext. |
| * @param aEObject EObject |
| * @param aUser R4EUser |
| */ |
| UpdateContext(EObject aEObject, String aUser) { |
| fEObject = aEObject; |
| fUser = aUser; |
| } |
| |
| /** |
| * Method getResource. |
| * @return Resource |
| */ |
| public Resource getResource() { |
| return fEObject.eResource(); |
| } |
| |
| /** |
| * Method getUser. |
| * @return R4EUser |
| */ |
| public String getUser() { |
| return fUser; |
| } |
| |
| /** |
| * Method getBookingNum. |
| * @return Long |
| */ |
| public Long getBookingNum() { |
| return fBookingNum; |
| } |
| |
| /** |
| * Method setBookingNum. |
| * @param aBookingNum Long |
| */ |
| public void setBookingNum(Long aBookingNum) { |
| fBookingNum = aBookingNum; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.lang.Object#equals(java.lang.Object) |
| */ |
| /** |
| * Method equals. |
| * @param o Object |
| * @return boolean |
| */ |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| |
| if (o instanceof UpdateContext) { |
| UpdateContext otherContext = (UpdateContext) o; |
| Resource otherResource = otherContext.getResource(); |
| if (otherResource != fEObject.eResource()) { |
| // resource references are different, check the contents to make sure |
| URI otherURI = otherResource.getURI(); |
| URI tURI = fEObject.eResource().getURI(); |
| if (!(otherURI.equals(tURI))) { |
| return false; |
| } |
| } |
| |
| // Compare user IDs |
| String oUserId = otherContext.getUser(); |
| if (oUserId.equals(fUser)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.lang.Object#hashCode() |
| */ |
| /** |
| * Method hashCode. |
| * @return int |
| */ |
| public int hashCode() { |
| String toHash = fEObject.eResource().getURI().toString() + fUser; |
| return toHash.hashCode(); |
| } |
| } |
| } |
| |