/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.persistence;
import java.beans.IntrospectionException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.UUID;
/**
* Abstract repository to be sub-classed, contains some generic functions
* @author jblok
*/
public abstract class AbstractRepository extends AbstractPersistFactory implements IDeveloperRepository
{
public static final int repository_version = 47;
/**
* This repository is used by client
*/
protected final String lock_id = UUID.randomUUID().toString();
protected RepositoryHelper repositoryHelper;
private UUID repository_uuid = null;
private final Map<String, String> mimeTypeMap;
protected final IServerManager serverManager;
protected AbstractRepository(IServerManager serverManager)
{
this.serverManager = serverManager;
mimeTypeMap = new HashMap<String, String>();
mimeTypeMap.put("gif", "image/gif"); //$NON-NLS-1$//$NON-NLS-2$
mimeTypeMap.put("png", "image/png"); //$NON-NLS-1$//$NON-NLS-2$
mimeTypeMap.put("jpg", "image/jpg"); //$NON-NLS-1$ //$NON-NLS-2$
repositoryHelper = new RepositoryHelper(this);
}
public String getContentType(String filename)
{
if (filename == null) return null;
int i = filename.lastIndexOf('.');
if (i < 0) return null;
return mimeTypeMap.get(filename.substring(i + 1).toLowerCase());
}
public void registerMimeTypes(Map<String, String> mimeTypes)
{
mimeTypeMap.putAll(mimeTypes);
}
public synchronized UUID getRepositoryUUID() throws RepositoryException
{
if (repository_uuid == null)
{
Properties props = getUserProperties(SYSTEM_USER_ID);
String rep_uuid = props.getProperty(REPOSITORY_UUID_PROPERTY_NAME);
if (rep_uuid != null)
{
repository_uuid = UUID.fromString(rep_uuid);
}
else
{
repository_uuid = UUID.randomUUID();
props.put(REPOSITORY_UUID_PROPERTY_NAME, repository_uuid.toString());
setUserProperties(SYSTEM_USER_ID, props);
}
}
return repository_uuid;
}
public abstract Properties getUserProperties(int user_id) throws RepositoryException; //user 0 is SYSTEM
public Map<String, Method> getGettersViaIntrospection(Object obj) throws IntrospectionException
{
return RepositoryHelper.getGettersViaIntrospection(obj);
}
public IPersist createNewPersistInSolution(Solution solution, UUID parentUUID, int typeID, int id, UUID uuid, Map<String, Object> values)
throws RepositoryException
{
IPersist parent = searchPersist(solution, parentUUID);
if (parent instanceof ISupportChilds)
{
IPersist persist = null;
// first try to find the persist/child, if it is not already loaded. (tablenodes will do that)
Iterator<IPersist> it = ((ISupportChilds)parent).getAllObjects();
while (it.hasNext())
{
IPersist child = it.next();
if (child.getUUID().equals(uuid))
{
persist = child;
break;
}
}
if (persist == null)
{
persist = solution.getChangeHandler().createNewObject((ISupportChilds)parent, typeID, id, uuid);
((ISupportChilds)parent).addChild(persist);
}
updatePersistWithValueMap(persist, values, false);
return persist;
}
return null;
}
/**
* Search the persist in the parent tree.
* <p>
* optimized using the assumption that the persist is in a tree of similar structure to the tree of parent.
*
* @param parent
* @param persist
* @throws RepositoryException
*/
@SuppressWarnings("unchecked")
public static <T extends IPersist> T searchPersist(ISupportChilds parent, T persist)
{
if (persist == null || parent == null) return null;
UUID parentUuid = parent.getUUID();
if (parentUuid.equals(persist.getUUID())) return (T)parent;
// find the path to the parent
List<UUID> path = new ArrayList<UUID>();
IPersist p = persist;
while (p != null)
{
path.add(p.getUUID());
if (p.getUUID().equals(parentUuid))
{
p = null;
}
else
{
p = p.getParent();
}
}
if (path.size() > 0 && parentUuid.equals(path.get(path.size() - 1)))
{
// find persist via same path
IPersist node = parent;
for (int i = path.size() - 2; node != null && i >= 0; i--)
{
node = ((ISupportChilds)node).getChild(path.get(i));
}
if (node != null)
{
return (T)node;
}
}
// not found via the path, check if the persist's (grand)parent is the parent, in that case the persist was deleted
p = persist;
while (p != null)
{
if (p == parent)
{
// persist was deleted, no need to do a complete search
return null;
}
p = p.getParent();
}
// not found via the path and in different parent, fall back on complete search on uuid
return (T)searchPersist(parent, persist.getUUID(), persist.getParent());
}
public static IPersist searchPersist(ISupportChilds node, final UUID uuid)
{
return searchPersist(node, uuid, null);
}
public static IPersist searchPersist(ISupportChilds node, final UUID uuid, final ISupportChilds searchParent)
{
if (node == null || uuid == null) return null;
if (node.getUUID().equals(uuid)) return node;
return (IPersist)node.acceptVisitor(new IPersistVisitor()
{
public Object visit(IPersist o)
{
if (o instanceof ISupportChilds)
{
if (searchParent == null || searchParent.equals(o))
{
IPersist child = ((ISupportChilds)o).getChild(uuid);
if (child != null)
{
return child;
}
}
return CONTINUE_TRAVERSAL;
}
return CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER;
}
});
}
/**
* Create a copy of a solution object without its children.
*
* @param solution
* @throws RepositoryException
*/
public Solution createSolutionCopy(Solution solution) throws RepositoryException
{
Solution solutionCopy = (Solution)createObject(null, solution.getTypeID(), solution.getID(), solution.getUUID());
solutionCopy.setChangeHandler(new ChangeHandler(this));
Map<String, Object> values = getPersistAsValueMap(solution);
updatePersistWithValueMap(solutionCopy, values, false);
return solutionCopy;
}
/**
* Copy a persist from another solution to the solution.
*
* @param persist
* @param dontSearch
* @param solution
* @throws RepositoryException
*/
public IPersist copyPersistIntoSolution(IPersist persist, ISupportChilds parent, boolean search) throws RepositoryException
{
IPersist destPersist = null;
if (parent.getUUID().equals(persist.getUUID()))
{
destPersist = parent;
}
else if (search)
{
destPersist = searchPersist(parent, persist);
}
if (destPersist == null)
{
// object was added
IPersist destParent = searchPersist(parent, persist.getParent());
if (!(destParent instanceof ISupportChilds))
{
// this should never happen, don't know how to fix this one
throw new RepositoryException("Could not update editing solution, please refresh the solution"); //$NON-NLS-1$
}
destPersist = createObject((ISupportChilds)destParent, persist.getTypeID(), persist.getID(), persist.getUUID());
((ISupportChilds)destParent).addChild(destPersist);
}
if (persist instanceof AbstractScriptProvider)
{
((AbstractScriptProvider)destPersist).setRuntimeProperty(IScriptProvider.METHOD_ARGUMENTS,
((AbstractScriptProvider)persist).getRuntimeProperty(IScriptProvider.METHOD_ARGUMENTS));
((AbstractScriptProvider)destPersist).setRuntimeProperty(IScriptProvider.COMMENT,
((AbstractScriptProvider)persist).getRuntimeProperty(IScriptProvider.COMMENT));
((AbstractScriptProvider)destPersist).setSerializableRuntimeProperty(IScriptProvider.TYPE,
((AbstractScriptProvider)persist).getSerializableRuntimeProperty(IScriptProvider.TYPE));
}
Map<String, Object> values = ((AbstractRepository)persist.getRootObject().getRepository()).getPersistAsValueMap(persist);
updatePersistWithValueMap(destPersist, values, true);
if (destPersist.isChanged())
{
destPersist.getRootObject().getChangeHandler().fireIPersistChanged(destPersist);
}
return destPersist;
}
/*
* ___________________________RootObjectCache methods
*/
private RootObjectCache rootObjectCache = null;
private static Object ROOT_OBJECT_SYNC = new Object();
public boolean isRootObjectCacheInitialized()
{
return rootObjectCache != null;
}
protected RootObjectCache getRootObjectCache() throws RepositoryException
{
try
{
synchronized (ROOT_OBJECT_SYNC)
{
if (rootObjectCache == null)
{
rootObjectCache = new RootObjectCache(this, loadRootObjectMetaDatas());
}
}
return rootObjectCache;
}
catch (Exception e)
{
throw new RepositoryException(e);
}
}
public void flush()
{
// will force a reload of RootObjectMetaDatas as well (not just caches for existing RootObjectMetaDatas)
synchronized (ROOT_OBJECT_SYNC)
{
rootObjectCache = null;
}
}
public void flushAllCachedData() throws RepositoryException
{
getRootObjectCache().flush();
}
public void flushRootObject(int rootObjectId) throws RepositoryException
{
getRootObjectCache().flushRootObject(rootObjectId);
}
public void flushRootObjectRelease(int rootObjectId, int release) throws RepositoryException
{
getRootObjectCache().flushRootObjectRelease(rootObjectId, release);
}
public void removeRootObject(int rootObjectId) throws RepositoryException
{
// Delete solution from cache...
getRootObjectCache().removeRootObject(rootObjectId);
}
public List<IRootObject> getActiveRootObjects(int objectTypeId) throws RepositoryException
{
try
{
RootObjectMetaData[] metadatas = getRootObjectMetaDatasForType(objectTypeId);
List<IRootObject> rootObjects = new ArrayList<IRootObject>(metadatas.length);
for (RootObjectMetaData element : metadatas)
{
rootObjects.add(getRootObject(element.getRootObjectId(), element.getActiveRelease()));
}
return rootObjects;
}
catch (Exception e)
{
throw new RepositoryException(e);
}
}
public SolutionMetaData[] getSolutionMetaDatas() throws RepositoryException
{
RootObjectMetaData[] metas = getRootObjectCache().getRootObjectMetaDatasForType(IRepository.SOLUTIONS);
SolutionMetaData[] result = new SolutionMetaData[metas.length];
System.arraycopy(metas, 0, result, 0, metas.length);
return result;
}
public RootObjectMetaData[] getRootObjectMetaDatas() throws RepositoryException
{
return getRootObjectCache().getRootObjectMetaDatas();
}
public RootObjectMetaData[] getRootObjectMetaDatasForType(int objectTypeId) throws RepositoryException
{
return getRootObjectCache().getRootObjectMetaDatasForType(objectTypeId);
}
public RootObjectMetaData getRootObjectMetaData(String name, int objectTypeId) throws RepositoryException
{
return getRootObjectCache().getRootObjectMetaData(name, objectTypeId);
}
public RootObjectMetaData getRootObjectMetaData(UUID uuid) throws RepositoryException
{
return getRootObjectCache().getRootObjectMetaData(getElementIdForUUID(uuid));
}
public RootObjectMetaData getRootObjectMetaData(int rootObjectId) throws RepositoryException
{
return getRootObjectCache().getRootObjectMetaData(rootObjectId);
}
public IRootObject getRootObject(int rootObjectId, int release) throws RepositoryException
{
return getRootObjectCache().getRootObject(rootObjectId, release);
}
public IRootObject getRootObject(String name, int objectTypeId, int release) throws RepositoryException
{
return getRootObjectCache().getRootObject(name, objectTypeId, release);
}
public IRootObject getActiveRootObject(int rootObjectId) throws RepositoryException
{
return getRootObjectCache().getActiveRootObject(rootObjectId);
}
public IRootObject getActiveRootObject(String name, int objectTypeId) throws RepositoryException
{
return getRootObjectCache().getActiveRootObject(name, objectTypeId);
}
public IRootObject getLatestRootObject(int rootObjectId) throws RepositoryException
{
RootObjectMetaData metadata = getRootObjectCache().getRootObjectMetaData(rootObjectId);
if (metadata == null)
{
try
{
Iterator<RootObjectMetaData> iterator = loadRootObjectMetaDatas().iterator();
while (iterator.hasNext())
{
metadata = iterator.next();
if (metadata.getRootObjectId() == rootObjectId)
{
getRootObjectCache().add(metadata, false);
return getRootObject(rootObjectId, metadata.getLatestRelease());
}
}
}
catch (Exception e)
{
Debug.error("Error loading new MetaData for rootobjectid: " + rootObjectId, e); //$NON-NLS-1$
}
return null;
}
return getRootObject(rootObjectId, metadata.getLatestRelease());
}
protected abstract Collection<RootObjectMetaData> loadRootObjectMetaDatas() throws Exception;
//Should only be called by rootobjectcache
protected abstract IRootObject loadRootObject(RootObjectMetaData romd, int releaseNumber) throws RepositoryException;
/*
* ___________________________Helper methods
*/
public Map<String, Object> getPersistAsValueMap(IPersist persist) throws RepositoryException
{
return ((AbstractBase)persist).getPropertiesMap();
}
public void updatePersistWithValueMap(IPersist persist, Map<String, Object> propertyValues, boolean overwrite) throws RepositoryException
{
((AbstractBase)persist).copyPropertiesMap(propertyValues, overwrite);
}
/**
* @see com.servoy.j2db.persistence.AbstractPersistFactory#initClone(com.servoy.j2db.persistence.IPersist, com.servoy.j2db.persistence.IPersist)
*/
@Override
public void initClone(IPersist clone, IPersist objToClone, boolean flattenOverrides) throws RepositoryException
{
RepositoryHelper.initClone(clone, objToClone, flattenOverrides);
}
/**
* @throws RepositoryException
* @see com.servoy.j2db.persistence.AbstractPersistFactory#createRootObject(int)
*/
@Override
protected IPersist createRootObject(int elementId) throws RepositoryException
{
RootObjectMetaData rootObjectMetaData = getRootObjectMetaData(elementId);
return createRootObject(rootObjectMetaData);
}
public IRootObject createRootObject(RootObjectMetaData metaData) throws RepositoryException
{
AbstractRootObject rootObject = null;
switch (metaData.getObjectTypeId())
{
case IRepository.SOLUTIONS :
SolutionMetaData smd = (SolutionMetaData)metaData;
rootObject = new Solution(this, smd);
break;
case IRepository.STYLES :
rootObject = new Style(this, metaData);
break;
case IRepository.TEMPLATES :
rootObject = new Template(this, metaData);
break;
default :
throw new RepositoryException("Invalid root object type!"); //$NON-NLS-1$
}
// Set the root object properties.
rootObject.setReleaseNumber(1);
rootObject.setRevisionNumber(1);
rootObject.setRepository(this);
return rootObject;
}
/**
* Create a new root object
*/
public IRootObject createNewRootObject(String name, int objectTypeId) throws RepositoryException
{
UUID uuid = UUID.randomUUID();
IRootObject rootObject = createNewRootObject(name, objectTypeId, getNewElementID(uuid), uuid);
// Put the root object in the cache.
return rootObject;
}
/**
* Remove persist from parent and register it and all children (recursive) as removed.
*
* @throws RepositoryException
*/
public void deleteObject(IPersist persist) throws RepositoryException
{
persist.acceptVisitor(new IPersistVisitor()
{
public Object visit(IPersist o)
{
if (o.getRootObject() instanceof AbstractRootObject)
{
((AbstractRootObject)o.getRootObject()).registerRemovedObject(o);
}
return IPersistVisitor.CONTINUE_TRAVERSAL;
}
});
persist.getParent().removeChild(persist);
}
/**
* Re-add previously removed persist to parent and unregister it and all its children (recursive) as new.
*
* @throws RepositoryException
*/
public void undeleteObject(ISupportChilds parent, IPersist persist) throws RepositoryException
{
persist.acceptVisitor(new IPersistVisitor()
{
public Object visit(IPersist o)
{
if (o.getRootObject() instanceof AbstractRootObject)
{
((AbstractRootObject)o.getRootObject()).registerNewObject(o);
o.flagChanged();
}
return IPersistVisitor.CONTINUE_TRAVERSAL;
}
});
parent.addChild(persist);
}
public abstract IRootObject createNewRootObject(String name, int objectTypeId, int newElementID, UUID uuid) throws RepositoryException;
public abstract void restoreObjectToCurrentRelease(IPersist persist) throws RepositoryException;
/*
* _____________________________________________________________ Server methods, note servers are not in version control!
*/
/**
* Load the server names which are defined in this repository<br>
* Will also return the name of the server which created this repository
*
* @return the defined server names
*/
public String[] getServerNames(boolean sort) throws RepositoryException
{
return serverManager.getServerNames(true, false, sort, false);
}
/**
* Returns only valid server interfaces.
* <p>
* <b>NOTE: NEVER call this method from client code, always use solution.getServer(name), databaseManager.switchServer() is based on this!<b>
*
* @return the server
*/
public IServer getServer(String name)
{
return serverManager.getServer(name, true, false);
}
public String[] getDuplicateServerNames(String name)
{
return serverManager.getDuplicateServerNames(name, true, false);
}
public IServer[] getValidDataModelCloneServers(String name)
{
if (name == null) return null;
IServer[] clones = serverManager.getDataModelCloneServers(name);
List<IServer> validClones = new ArrayList<IServer>();
if (clones != null && clones.length > 0)
{
for (IServer clone : clones)
{
try
{
if (serverManager.getServer(clone.getName(), true, true) != null)
{
// valid server
validClones.add(clone);
}
}
catch (Exception ex)
{
Debug.error(ex);
}
}
}
return validClones.toArray(new IServer[0]);
}
public Map<String, IServer> getServerProxies(RootObjectMetaData[] metas) throws RepositoryException
{
Map<String, IServer> retval = new HashMap<String, IServer>();
for (RootObjectMetaData element : metas)
{
Solution s = (Solution)getRootObject(element.getRootObjectId(), element.getActiveRelease());
Map<String, IServer> sps = s.getServerProxies();
synchronized (sps)
{
retval.putAll(sps);
}
}
return retval;
}
public List<RootObjectReference> getActiveSolutionModuleMetaDatas(int solutionId) throws RepositoryException
{
return repositoryHelper.getActiveSolutionModuleMetaDatas(solutionId);
}
public RootObjectMetaData createRootObjectMetaData(int rootObjectId, UUID rootObjectUuid, String name, int objectTypeId, int activeRelease,
int latestRelease)
{
switch (objectTypeId)
{
case IRepository.SOLUTIONS :
return new SolutionMetaData(rootObjectId, rootObjectUuid, name, objectTypeId, activeRelease, latestRelease);
default :
return new RootObjectMetaData(rootObjectId, rootObjectUuid, name, objectTypeId, activeRelease, latestRelease);
}
}
public RootObjectMetaData createNewRootObjectMetaData(int rootObjectId, UUID rootObjectUuid, String name, int objectTypeId, int activeRelease,
int latestRelease) throws RepositoryException
{
RootObjectMetaData romd = createRootObjectMetaData(rootObjectId, rootObjectUuid, name, objectTypeId, activeRelease, latestRelease);
getRootObjectCache().add(romd, false);
return romd;
}
/**
* Adds the given meta data object to the root object meta data cache.
*
* @param rootObjectMetaData the meta data to be added.
*/
public void addRootObjectMetaData(RootObjectMetaData rootObjectMetaData) throws RepositoryException
{
getRootObjectCache().add(rootObjectMetaData, true);
}
// use this with caution, will not load all rootobjects
public void cacheRootObject(IRootObject rootObject) throws RepositoryException
{
try
{
synchronized (ROOT_OBJECT_SYNC)
{
if (rootObjectCache == null)
{
rootObjectCache = new RootObjectCache(this, new ArrayList());
}
}
}
catch (Exception e)
{
throw new RepositoryException(e);
}
addRootObjectMetaData(rootObject.getRootObjectMetaData());
getRootObjectCache().cacheRootObject(rootObject);
}
public abstract void updateRootObject(IRootObject rootObject) throws RepositoryException;
}