/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.system.tools;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.deployers.vfs.spi.structure.modified.StructureModificationChecker;
import org.jboss.logging.Logger;
import org.jboss.profileservice.spi.ModificationInfo;
import org.jboss.profileservice.spi.MutableProfile;
import org.jboss.profileservice.spi.NoSuchProfileException;
import org.jboss.profileservice.spi.Profile;
import org.jboss.profileservice.spi.ProfileDeployment;
import org.jboss.profileservice.spi.ProfileKey;
import org.jboss.profileservice.spi.ProfileService;
import org.jboss.system.server.profileservice.hotdeploy.Scanner;
import org.jboss.system.server.profileservice.repository.HotDeploymentRepository;
import org.jboss.system.server.profileservice.repository.TypedProfileRepository;
import org.jboss.virtual.VFS;
import org.jboss.virtual.VirtualFile;
/**
* Profile deployment repository adapter.
*
* @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a>
* @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
*/
public class ProfileServiceDeploymentRepositoryAdapter implements DeploymentRepositoryAdapter
{
private static final Logger log = Logger.getLogger(ProfileServiceDeploymentRepositoryAdapter.class);
private static final ProfileKey profileName = new ProfileKey("deployment-scanner-profile");
private Scanner scanner;
private ProfileService ps;
private TypedProfileRepository repositories;
private StructureModificationChecker checker;
private DeploymentScannerProfile profile;
public ProfileServiceDeploymentRepositoryAdapter(Scanner scanner, ProfileService ps, StructureModificationChecker checker)
{
if (scanner == null)
throw new IllegalArgumentException("Null scanner");
if (ps == null)
throw new IllegalArgumentException("Null profile service");
if (checker == null)
throw new IllegalArgumentException("Null structure checker");
this.scanner = scanner;
this.ps = ps;
this.checker = checker;
}
public TypedProfileRepository getProfileRepository()
{
return repositories;
}
public void setProfileRepository(TypedProfileRepository repositories)
{
this.repositories = repositories;
}
/**
* Create profile.
*
* @throws Exception for any error
*/
public void create() throws Exception
{
this.profile = new DeploymentScannerProfile(checker);
// Create the profile
registerProfile();
}
/**
* Stop profile.
*/
public void destroy()
{
stopProfile();
}
/**
* Register profile.
*
* @throws Exception for any error
*/
protected void registerProfile() throws Exception
{
if(this.ps == null)
throw new IllegalStateException("Null profile service.");
// Register
this.ps.registerProfile(profile);
// Activate
log.debug("Activating deployment scanner profile " + profileName);
this.ps.activateProfile(profileName);
this.ps.validateProfile(profileName);
// Expose the profile to the DeploymentManager
if(this.repositories != null)
this.repositories.registerDeploymentRepository(profile);
}
public void resume()
{
scanner.resume();
}
public void suspend()
{
scanner.suspend();
}
/**
* Stop deactivates and unregisters the transient deployments profile.
*/
public void stopProfile()
{
// Unregister the profile
if(this.repositories != null)
this.repositories.unregisterDeploymentRepository(profile);
try
{
// Deactivate
log.debug("Deactivating deployment scanner profile: " + profileName);
this.ps.deactivateProfile(profileName);
}
catch(Exception e)
{
log.debug("Failed to deactivate deployment scanner profile: ", e);
}
try
{
// Unregister
log.debug("Unregistering transient profile: " + profileName);
this.ps.unregisterProfile(profileName);
}
catch(Exception e)
{
log.debug("Failed to unregister deployment scanner profile: ", e);
}
}
public void addURL(URL url) throws URISyntaxException
{
URI uri = url.toURI();
Collection<URI> uris = profile.getURIs();
if (uris.contains(uri) == false)
{
uris.add(uri);
}
}
public void removeURL(URL url) throws URISyntaxException
{
URI uri = url.toURI();
Collection<URI> uris = profile.getURIs();
uris.remove(uri);
}
public boolean hasURL(URL url) throws URISyntaxException
{
URI uri = url.toURI();
// TODO - this only checks this profile
return profile.getURIs().contains(uri);
}
public String[] listDeployedURLs()
{
List<String> urls = new ArrayList<String>();
Collection<ProfileKey> activeProfiles = ps.getActiveProfileKeys();
if (activeProfiles != null && activeProfiles.isEmpty() == false)
{
for (ProfileKey key : activeProfiles)
{
// The profile
Profile profile;
try
{
profile = ps.getActiveProfile(key);
}
catch (NoSuchProfileException ignore)
{
continue;
}
Collection<ProfileDeployment> deployments = profile.getDeployments();
if (deployments != null && deployments.isEmpty() == false)
{
for (ProfileDeployment pd : deployments)
{
VirtualFile root = pd.getRoot();
if (root != null)
{
try
{
urls.add(root.toURL().toExternalForm());
}
catch (Exception e)
{
log.warn("Exception while reading root's URL: " + root);
}
}
}
}
}
}
return urls.toArray(new String[urls.size()]);
}
public static class DeploymentScannerProfile extends HotDeploymentRepository implements MutableProfile
{
private volatile boolean enableHotDeployment;
private Map<URI, List<VirtualFile>> oldCache = new ConcurrentHashMap<URI, List<VirtualFile>>();
private Map<URI, List<VirtualFile>> newCache = new ConcurrentHashMap<URI, List<VirtualFile>>();
public DeploymentScannerProfile(StructureModificationChecker checker)
{
super(profileName, new URI[0]);
setChecker(checker);
}
/**
* Expose uris.
*
* @return the uris
*/
Collection<URI> getURIs()
{
return uris;
}
public void addDeployment(ProfileDeployment deployment) throws Exception
{
super.addDeployment(deployment.getName(), deployment);
}
public void enableModifiedDeploymentChecks(boolean flag)
{
this.enableHotDeployment = flag;
}
@Override
public Collection<ModificationInfo> getModifiedDeployments() throws Exception
{
if(this.enableHotDeployment == false)
return Collections.emptySet();
return super.getModifiedDeployments();
}
@Override
protected void checkForAdditions(List<ModificationInfo> modified) throws Exception
{
// clear new cache
newCache.clear();
// do real check
super.checkForAdditions(modified);
// remove the old stuff - what's left of it
long lastModified = System.currentTimeMillis();
for (List<VirtualFile> files : oldCache.values())
{
for (VirtualFile file : files)
{
// the key is URI
String name = file.toURI().toString();
// it still exists - remove it
if (acceptsDeployment(name) == false)
{
unlockRead();
ProfileDeployment previous;
try
{
// the actual removal, but we don't delete the file
previous = removeDeployment(name, false);
}
finally
{
lockRead();
}
ModificationInfo removed = new ModificationInfo(previous, lastModified, ModificationInfo.ModifyStatus.REMOVED);
modified.add(removed);
}
}
}
// switch new --> old
oldCache.clear();
oldCache.putAll(newCache);
}
@Override
protected void applyAddedDeployments(URI applicationDir, List<ModificationInfo> modified, List<VirtualFile> added) throws Exception
{
// remove from old cache - it exists
List<VirtualFile> files = oldCache.remove(applicationDir);
// do real apply
super.applyAddedDeployments(applicationDir, modified, added);
// add to old + put to new
if (files == null)
files = new ArrayList<VirtualFile>(added);
else
files.addAll(added);
newCache.put(applicationDir, files);
}
@Override
protected List<String> findDeploymentContent(String name)
{
// FIXME this should not be done here
// Try to find the VirtualFile, as we only add real file urls
VirtualFile cached = getCachedVirtualFile(name);
if(cached != null)
{
try {
name = cached.toURI().toString();
} catch(Exception ignore) { }
return Collections.singletonList(name);
}
List<String> contents = new ArrayList<String>();
for(URI uri : this.uris)
{
String cacheName = uri.toString();
String fixedName = cacheName;
if(cacheName.endsWith("/"))
fixedName = cacheName.substring(0, cacheName.length() -1);
if(fixedName.endsWith(name))
{
VirtualFile vf = getCachedVirtualFile(cacheName);
if(vf != null)
{
try {
contents.add(vf.toURI().toString());
} catch(Exception ignore) { }
}
}
}
return contents;
}
@Override
public ProfileDeployment removeDeployment(String vfsPath) throws Exception
{
// We don't remove the actual deployment content
return super.removeDeployment(vfsPath, false);
}
public ProfileKey getKey()
{
return profileName;
}
public Collection<ProfileKey> getSubProfiles()
{
return Collections.emptySet();
}
public boolean hasDeployment(String name)
{
// FIXME
return false;
}
public boolean isMutable()
{
return true;
}
}
}