/* * * Goko * Copyright (C) 2013 PsyKo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.goko.core.workspace.service; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections.CollectionUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.goko.core.common.exception.GkException; import org.goko.core.common.exception.GkTechnicalException; import org.goko.core.common.io.xml.IXmlPersistenceService; import org.goko.core.common.service.AbstractGokoService; import org.goko.core.log.GkLog; import org.goko.core.workspace.element.GkProject; import org.goko.core.workspace.io.IProjectInputLocation; import org.goko.core.workspace.io.IProjectLocation; import org.goko.core.workspace.io.IResourceLocation; import org.goko.core.workspace.io.ProjectLocation; import org.goko.core.workspace.io.XmlGkProject; import org.goko.core.workspace.io.XmlProjectContainer; import org.goko.core.workspace.io.xml.XmlURIResourceLocation; import org.goko.core.workspace.mapper.URIResourceLocationExporter; import org.goko.core.workspace.mapper.URIResourceLocationLoader; /** * Default implementation of the workspace service * * @author PsyKo * */ public class WorkspaceService extends AbstractGokoService implements IWorkspaceService{ /** LOG */ private static final GkLog LOG = GkLog.getLogger(WorkspaceService.class); /** Service ID */ private static final String SERVICE_ID ="org.goko.core.workspace.WorkspaceService"; /** The list of listener */ private List<IWorkspaceListener> listenerList; /** The list of listener for the project lifecycle */ private List<IProjectLifecycleListener> projectLifecycleListenerList; /** The known save participants */ private List<IProjectSaveParticipant<?>> saveParticipants; /** The known load participants */ private List<IProjectLoadParticipant> loadParticipants; // Temporary project storage private GkProject project; /** The xml persistence service */ private IXmlPersistenceService xmlPersistenceService; /** The mapper service */ private IMapperService mapperService; /** (inheritDoc) * @see org.goko.core.common.service.IGokoService#getServiceId() */ @Override public String getServiceId() throws GkException { return SERVICE_ID; } /** (inheritDoc) * @see org.goko.core.common.service.IGokoService#start() */ @Override public void startService() throws GkException { this.listenerList = new ArrayList<IWorkspaceListener>(); this.project = new GkProject(); this.project.setLocation( new ProjectLocation() ); this.projectLifecycleListenerList = new ArrayList<IProjectLifecycleListener>(); this.mapperService.addLoader(new URIResourceLocationLoader(this)); this.mapperService.addExporter(new URIResourceLocationExporter()); this.xmlPersistenceService.register(XmlURIResourceLocation.class); } /** (inheritDoc) * @see org.goko.core.common.service.IGokoService#stop() */ @Override public void stopService() throws GkException { } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#addResource(java.net.URI) */ @Override public IResourceLocation addResource(URI uri) throws GkException { return project.getLocation().addResource(uri.toString(), uri); } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#addWorkspaceListener(IWorkspaceListener) */ @Override public void addWorkspaceListener(IWorkspaceListener listener) throws GkException { this.listenerList.add(listener); } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#removeWorkspaceListener(org.goko.core.workspace.service.IWorkspaceListener) */ @Override public void removeWorkspaceListener(IWorkspaceListener listener) throws GkException { this.listenerList.remove(listener); } @Override public void notifyWorkspaceEvent(IWorkspaceEvent event) throws GkException{ notifyListeners(event); } /** * Notify the registered listeners with the given event * @param event the event * @throws GkException GkException */ private void notifyListeners(IWorkspaceEvent event) throws GkException { if(CollectionUtils.isNotEmpty(listenerList)){ for (IWorkspaceListener workspaceListener : listenerList) { workspaceListener.onWorkspaceEvent(event); } } } /** * @return the project */ @Override public GkProject getProject() { return project; } /** * @param project the project to set */ public void setProject(GkProject project) { this.project = project; } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#addProjectSaveParticipant(org.goko.core.workspace.service.IProjectSaveParticipant) */ @Override public void addProjectSaveParticipant(IProjectSaveParticipant<?> participant) throws GkException { this.getSaveParticipants().add(participant); } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#addProjectLoadParticipant(org.goko.core.workspace.service.IProjectLoadParticipant) */ @Override public void addProjectLoadParticipant(IProjectLoadParticipant participant) throws GkException { this.getLoadParticipants().add(participant); } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#saveProject() */ @Override public void saveProject(IProjectLocation output, IProgressMonitor monitor) throws GkException { LOG.info("Saving project to "+output.getLocation()); // Notify listeners notifyProjectBeforeSave(); XmlGkProject xmlProject = new XmlGkProject(); project.getLocation().importProjectDependencies(); for (IProjectSaveParticipant<?> saveParticipant : getSaveParticipants()) { xmlProject.getProjectContainer().addAll(saveParticipant.save(output)); } try { ByteArrayOutputStream oStream = new ByteArrayOutputStream(); xmlPersistenceService.write(xmlProject, oStream); output.setProjectDescriptor(new ByteArrayInputStream(oStream.toByteArray())); // Persist the output output.write(); } catch (GkException e) { rollbackSaveProject(); throw e; } project.setLocation(output); project.setName(project.getLocation().getName()); completeSaveProject(); // Notify listeners notifyProjectAfterSave(); // Mark project as not dirty setProjectDirty(false); LOG.info("Project save complete."); } /** * Utility method to change the project dirty state and notify the listeners * @param dirty dirty state * @throws GkException GkException */ private void setProjectDirty(boolean dirty) throws GkException { this.project.setDirty(dirty); notifyProjectDirtyStateChange(); } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#markProjectDirty() */ @Override public void markProjectDirty() throws GkException { setProjectDirty(true); } /** * Notifies all the save participant that the saved occurred without issue */ private void completeSaveProject(){ for (IProjectSaveParticipant<?> saveParticipant : getSaveParticipants()) { saveParticipant.saveComplete(); } } /** * Notifies all the save participant that the saved failed */ private void rollbackSaveProject(){ for (IProjectSaveParticipant<?> saveParticipant : getSaveParticipants()) { saveParticipant.rollback(); } } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#createNewProject() */ @Override public void createNewProject() throws GkException { if(CollectionUtils.isNotEmpty(getLoadParticipants())){ // Clear stored data first for (IProjectLoadParticipant loadParticipant : getLoadParticipants()) { loadParticipant.clearContent(); } } project = new GkProject(); project.setLocation( new ProjectLocation() ); setProjectDirty(false); // Notify listeners notifyProjectAfterLoad(); } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#loadProject(IProjectInputLocation) */ @Override public void loadProject(IProjectLocation input, IProgressMonitor monitor) throws GkException { try { input.read(); // Notify listeners notifyProjectBeforeLoad(); // Read the project description file InputStream projectFileInputStream = input.getProjectDescriptor(); XmlGkProject xmlGkProject = xmlPersistenceService.read(XmlGkProject.class, projectFileInputStream); projectFileInputStream.close(); project = new GkProject(); project.setName(input.getName()); project.setLocation(input); Map<String, XmlProjectContainer> mapContainerByType = new HashMap<>(); if(CollectionUtils.isNotEmpty(xmlGkProject.getProjectContainer())){ for (XmlProjectContainer projectContainer : xmlGkProject.getProjectContainer()) { mapContainerByType.put(projectContainer.getType(), projectContainer); } } // Sort by priority Collections.sort(getLoadParticipants(), new ProjectLoadParticipantComparator()); if(CollectionUtils.isNotEmpty(getLoadParticipants())){ // Clear stored data first for (IProjectLoadParticipant loadParticipant : getLoadParticipants()) { loadParticipant.clearContent(); } // Load new data for (IProjectLoadParticipant loadParticipant : getLoadParticipants()) { if(mapContainerByType.containsKey(loadParticipant.getContainerType())){ loadParticipant.load(mapContainerByType.get(loadParticipant.getContainerType()), input, monitor); } } } setProjectDirty(false); // Notify listeners notifyProjectAfterLoad(); } catch (Exception e) { throw new GkTechnicalException(e); } } protected void notifyProjectBeforeSave() throws GkException{ if(CollectionUtils.isNotEmpty(projectLifecycleListenerList)){ for (IProjectLifecycleListener listener : projectLifecycleListenerList) { listener.beforeSave(); } } } protected void notifyProjectAfterSave() throws GkException{ if(CollectionUtils.isNotEmpty(projectLifecycleListenerList)){ for (IProjectLifecycleListener listener : projectLifecycleListenerList) { listener.afterSave(); } } } protected void notifyProjectBeforeLoad() throws GkException{ if(CollectionUtils.isNotEmpty(projectLifecycleListenerList)){ for (IProjectLifecycleListener listener : projectLifecycleListenerList) { listener.beforeLoad(); } } } protected void notifyProjectDirtyStateChange() throws GkException{ if(CollectionUtils.isNotEmpty(projectLifecycleListenerList)){ for (IProjectLifecycleListener listener : projectLifecycleListenerList) { listener.onProjectDirtyStateChange(); } } } protected void notifyProjectAfterLoad() throws GkException{ if(CollectionUtils.isNotEmpty(projectLifecycleListenerList)){ for (IProjectLifecycleListener listener : projectLifecycleListenerList) { listener.afterLoad(); } } } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#addProjectLifecycleListener(org.goko.core.workspace.service.IProjectLifecycleListener) */ @Override public void addProjectLifecycleListener(IProjectLifecycleListener listener) throws GkException { this.projectLifecycleListenerList.add(listener); } /** (inheritDoc) * @see org.goko.core.workspace.service.IWorkspaceService#removeProjectLifecycleListener(org.goko.core.workspace.service.IProjectLifecycleListener) */ @Override public void removeProjectLifecycleListener(IProjectLifecycleListener listener) throws GkException { this.projectLifecycleListenerList.remove(listener); } /** * @return the xmlPersistenceService */ public IXmlPersistenceService getXmlPersistenceService() { return xmlPersistenceService; } /** * @param xmlPersistenceService the xmlPersistenceService to set */ public void setXmlPersistenceService(IXmlPersistenceService xmlPersistenceService) { this.xmlPersistenceService = xmlPersistenceService; } /** * @return the ssaveParticipants */ public List<IProjectSaveParticipant<?>> getSaveParticipants() { if(saveParticipants == null){ saveParticipants = new ArrayList<IProjectSaveParticipant<?>>(); } return saveParticipants; } /** * @return the loadParticipants */ private List<IProjectLoadParticipant> getLoadParticipants() { if(loadParticipants == null){ loadParticipants = new ArrayList<IProjectLoadParticipant>(); } return loadParticipants; } /** * @return the mapperService */ public IMapperService getMapperService() { return mapperService; } /** * @param mapperService the mapperService to set */ public void setMapperService(IMapperService mapperService) { this.mapperService = mapperService; } }