blob: 59a8471328c7bfb2cfa0058029124fd00723b014 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ace.webui.vaadin;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.ace.authentication.api.AuthenticationService;
import org.apache.ace.client.repository.RepositoryAdmin;
import org.apache.ace.client.repository.RepositoryAdminLoginContext;
import org.apache.ace.client.repository.RepositoryObject;
import org.apache.ace.client.repository.SessionFactory;
import org.apache.ace.client.repository.helper.bundle.BundleHelper;
import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
import org.apache.ace.client.repository.object.ArtifactObject;
import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
import org.apache.ace.client.repository.object.DistributionObject;
import org.apache.ace.client.repository.object.Feature2DistributionAssociation;
import org.apache.ace.client.repository.object.FeatureObject;
import org.apache.ace.client.repository.object.TargetObject;
import org.apache.ace.client.repository.repository.Artifact2FeatureAssociationRepository;
import org.apache.ace.client.repository.repository.ArtifactRepository;
import org.apache.ace.client.repository.repository.ArtifactRepository.ArtifactAlreadyExistsException;
import org.apache.ace.client.repository.repository.Distribution2TargetAssociationRepository;
import org.apache.ace.client.repository.repository.DistributionRepository;
import org.apache.ace.client.repository.repository.Feature2DistributionAssociationRepository;
import org.apache.ace.client.repository.repository.FeatureRepository;
import org.apache.ace.client.repository.repository.TargetRepository;
import org.apache.ace.client.repository.stateful.StatefulTargetObject;
import org.apache.ace.client.repository.stateful.StatefulTargetRepository;
import org.apache.ace.connectionfactory.ConnectionFactory;
import org.apache.ace.webui.NamedObject;
import org.apache.ace.webui.UIExtensionFactory;
import org.apache.ace.webui.domain.NamedStatefulTargetObject;
import org.apache.ace.webui.domain.NamedTargetObject;
import org.apache.ace.webui.vaadin.LoginWindow.LoginFunction;
import org.apache.ace.webui.vaadin.UploadHelper.ArtifactDropHandler;
import org.apache.ace.webui.vaadin.UploadHelper.GenericUploadHandler;
import org.apache.ace.webui.vaadin.UploadHelper.UploadHandle;
import org.apache.ace.webui.vaadin.component.ArtifactsPanel;
import org.apache.ace.webui.vaadin.component.AssociationHelper;
import org.apache.ace.webui.vaadin.component.DistributionsPanel;
import org.apache.ace.webui.vaadin.component.FeaturesPanel;
import org.apache.ace.webui.vaadin.component.MainActionToolbar;
import org.apache.ace.webui.vaadin.component.StatusLine;
import org.apache.ace.webui.vaadin.component.TargetsPanel;
import org.apache.felix.dm.Component;
import org.apache.felix.dm.DependencyManager;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.osgi.service.log.LogService;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.event.ShortcutAction.ModifierKey;
import com.vaadin.service.ApplicationContext;
import com.vaadin.terminal.gwt.server.WebApplicationContext;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.DragAndDropWrapper;
import com.vaadin.ui.DragAndDropWrapper.DragStartMode;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.ProgressIndicator;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.Notification;
/**
* Main application entry point.
*/
public class VaadinClient extends com.vaadin.Application implements AssociationManager, LoginFunction {
// basic session ID generator
private static long generateSessionID() {
return SESSION_ID.getAndIncrement();
}
/**
* Remove the given directory and all it's files and subdirectories
*
* @param directory
* the name of the directory to remove
*/
private static void removeDirectoryWithContent(File directory) {
if ((directory == null) || !directory.exists()) {
return;
}
File[] filesAndSubDirs = directory.listFiles();
for (int i = 0; i < filesAndSubDirs.length; i++) {
File file = filesAndSubDirs[i];
if (file.isDirectory()) {
removeDirectoryWithContent(file);
}
// else just remove the file
file.delete();
}
directory.delete();
}
private static final long serialVersionUID = 1L;
private static final AtomicLong SESSION_ID = new AtomicLong(1L);
private static final String targetRepo = "target";
private static final String shopRepo = "shop";
private static final String deployRepo = "deployment";
private static final String customerName = "apache";
private static final String endpoint = "/repository";
private volatile AuthenticationService m_authenticationService;
private volatile BundleContext m_context;
private volatile SessionFactory m_sessionFactory;
private volatile UserAdmin m_userAdmin;
private volatile ArtifactRepository m_artifactRepository;
private volatile FeatureRepository m_featureRepository;
private volatile DistributionRepository m_distributionRepository;
private volatile StatefulTargetRepository m_statefulTargetRepository;
private volatile TargetRepository m_targetRepository;
private volatile Artifact2FeatureAssociationRepository m_artifact2featureAssociationRepository;
private volatile Feature2DistributionAssociationRepository m_feature2distributionAssociationRepository;
private volatile Distribution2TargetAssociationRepository m_distribution2targetAssociationRepository;
private volatile RepositoryAdmin m_admin;
private volatile LogService m_log;
private volatile ConnectionFactory m_connectionFactory;
private String m_sessionID;
private ArtifactsPanel m_artifactsPanel;
private FeaturesPanel m_featuresPanel;
private DistributionsPanel m_distributionsPanel;
private TargetsPanel m_targetsPanel;
private GridLayout m_grid;
private StatusLine m_statusLine;
private File m_sessionDir; // private folder for session info
private HorizontalLayout m_artifactToolbar;
private HorizontalLayout m_featureToolbar;
private HorizontalLayout m_distributionToolbar;
private HorizontalLayout m_targetToolbar;
private Window m_mainWindow;
private final URL m_obrUrl;
private final String m_repositoryXML;
private final URL m_repository;
private final boolean m_useAuth;
private final String m_userName;
private final AssociationHelper m_associations = new AssociationHelper();
private final AtomicBoolean m_dependenciesResolved = new AtomicBoolean(false);
// for the artifacts list...
private final double m_cacheRate;
private final int m_pageLength;
private ProgressIndicator m_progress;
private DependencyManager m_manager;
private Component m_component;
private final List<Component> m_eventHandlers = new ArrayList<>();
private GridLayout m_mainToolbar;
/**
* Creates a new {@link VaadinClient} instance.
*
* @param m_manager2
*
* @param aceHost
* the hostname where the management service can be reached;
* @param obrUrl
* the URL of the OBR to use;
* @param useAuth
* <code>true</code> to use authentication, <code>false</code> to disable authentication;
* @param userName
* the hardcoded username to use when authentication is disabled.
*/
public VaadinClient(DependencyManager manager, URL aceHost, URL obrUrl, String repositoryXML, boolean useAuth, String userName, String password, double cacheRate, int pageLength) {
m_manager = manager;
try {
m_repository = new URL(aceHost, endpoint);
}
catch (MalformedURLException e) {
throw new IllegalArgumentException("Need a valid repository URL!", e);
}
m_obrUrl = obrUrl;
m_repositoryXML = repositoryXML;
m_useAuth = useAuth;
m_userName = userName;
m_cacheRate = cacheRate;
m_pageLength = pageLength;
if (!m_useAuth && (m_userName == null || "".equals(m_userName.trim()))) {
throw new IllegalArgumentException("Need a valid user name when no authentication is used!");
}
}
@Override
public void close() {
if (isRunning()) {
m_admin.deleteLocal();
cleanupListeners();
m_manager.remove(m_component);
super.close();
}
}
@Override
public Artifact2FeatureAssociation createArtifact2FeatureAssociation(String artifactId, String featureId) {
boolean dynamicRelation = false;
FeatureObject feature = m_featureRepository.get(featureId);
ArtifactObject artifact = m_artifactRepository.get(artifactId);
if (artifact == null) {
// Maybe a BSN?
try {
List<ArtifactObject> artifacts = m_artifactRepository.get(FrameworkUtil.createFilter(String.format("(%s=%s)", Constants.BUNDLE_SYMBOLICNAME, artifactId)));
if (artifacts != null && artifacts.size() > 0) {
dynamicRelation = true;
// we only need this artifact for creating the association, so it does not matter which one we
// take...
artifact = artifacts.get(0);
}
}
catch (InvalidSyntaxException exception) {
m_log.log(LogService.LOG_ERROR, "Invalid filter syntax?!", exception);
}
}
// Make sure we didn't drop on a resource processor bundle...
if (artifact != null && artifact.getAttribute(BundleHelper.KEY_RESOURCE_PROCESSOR_PID) != null) {
// if you drop on a resource processor, and try to get it, you
// will get null because you cannot associate anything with a
// resource processor so we check for null here
return null;
}
Artifact2FeatureAssociation result = null;
if (artifact != null) {
if (dynamicRelation) {
Map<String, String> properties = Collections.singletonMap(BundleHelper.KEY_ASSOCIATION_VERSIONSTATEMENT, "0.0.0");
result = m_artifact2featureAssociationRepository.create(artifact, properties, feature, null);
}
else {
result = m_artifact2featureAssociationRepository.create(artifact, feature);
}
}
return result;
}
@Override
public Distribution2TargetAssociation createDistribution2TargetAssociation(String distributionId, String targetId) {
DistributionObject distribution = m_distributionRepository.get(distributionId);
StatefulTargetObject target = m_statefulTargetRepository.get(targetId);
if (!target.isRegistered()) {
target.register();
target.setAutoApprove(true);
}
return m_distribution2targetAssociationRepository.create(distribution, target.getTargetObject());
}
@Override
public Feature2DistributionAssociation createFeature2DistributionAssociation(String featureId, String distributionId) {
FeatureObject feature = m_featureRepository.get(featureId);
DistributionObject distribution = m_distributionRepository.get(distributionId);
return m_feature2distributionAssociationRepository.create(feature, distribution);
}
public void destroyDependencies() {
m_sessionFactory.destroySession(m_sessionID);
removeDirectoryWithContent(m_sessionDir);
}
public void init() {
setTheme("ace");
if (!m_dependenciesResolved.get()) {
final Window message = new Window("Apache ACE");
message.getContent().setSizeFull();
setMainWindow(message);
Label richText = new Label("<h1>Apache ACE User Interface</h1>"
+ "<p>Due to missing component dependencies on the server, probably due to misconfiguration, "
+ "the user interface cannot be properly started. Please contact your server administrator. "
+ "You can retry accessing the user interface by <a href=\"?restartApplication\">following this link</a>.</p>");
richText.setContentMode(Label.CONTENT_XHTML);
// TODO we might want to add some more details here as to what's
// missing on the other hand, the user probably can't fix that anyway
message.addComponent(richText);
return;
}
m_mainWindow = new Window("Apache ACE");
m_mainWindow.getContent().setSizeFull();
m_mainWindow.setBorder(Window.BORDER_NONE);
setMainWindow(m_mainWindow);
// Authenticate the user either by showing a login window; or by another means...
authenticate();
}
/**
* {@inheritDoc}
*/
public boolean login(String username, String password) {
setUser(m_authenticationService.authenticate(username, password));
return doLogin();
}
/**
* {@inheritDoc}
*/
public void removeAssociation(Artifact2FeatureAssociation association) {
m_artifact2featureAssociationRepository.remove(association);
}
/**
* {@inheritDoc}
*/
public void removeAssociation(Distribution2TargetAssociation association) {
m_distribution2targetAssociationRepository.remove(association);
}
/**
* {@inheritDoc}
*/
public void removeAssociation(Feature2DistributionAssociation association) {
m_feature2distributionAssociationRepository.remove(association);
}
public void setupDependencies(Component component) {
m_sessionID = "web-" + generateSessionID();
File dir = m_context.getDataFile(m_sessionID);
dir.mkdir();
m_sessionDir = dir.getAbsoluteFile();
m_sessionFactory.createSession(m_sessionID, null);
addSessionDependency(component, RepositoryAdmin.class);
addSessionDependency(component, DistributionRepository.class);
addSessionDependency(component, ArtifactRepository.class);
addSessionDependency(component, FeatureRepository.class);
addSessionDependency(component, Artifact2FeatureAssociationRepository.class);
addSessionDependency(component, Feature2DistributionAssociationRepository.class);
addSessionDependency(component, Distribution2TargetAssociationRepository.class);
addSessionDependency(component, TargetRepository.class);
addSessionDependency(component, StatefulTargetRepository.class);
addDependency(component, ConnectionFactory.class);
}
public void start() {
m_log.log(LogService.LOG_INFO, "Starting session #" + m_sessionID);
m_dependenciesResolved.set(true);
}
@Override
public void start(URL applicationUrl, Properties applicationProperties, ApplicationContext context) {
m_component = m_manager.createComponent()
.setImplementation(this)
.setCallbacks("setupDependencies", "start", "stop", "destroyDependencies")
.add(m_manager.createServiceDependency()
.setService(SessionFactory.class)
.setRequired(true)
)
.add(m_manager.createServiceDependency()
.setService(UserAdmin.class)
.setRequired(true)
)
.add(m_manager.createServiceDependency()
.setService(AuthenticationService.class)
.setRequired(m_useAuth)
)
.add(m_manager.createServiceDependency()
.setService(LogService.class)
.setRequired(false)
);
m_manager.add(m_component);
super.start(applicationUrl, applicationProperties, context);
}
public void stop() throws Exception {
m_log.log(LogService.LOG_INFO, "Stopping session #" + m_sessionID);
try {
close();
try {
m_admin.logout(true /* force */);
}
catch (IllegalStateException exception) {
// Ignore, we're already logged out...
}
}
finally {
m_dependenciesResolved.set(false);
}
}
final void showAddArtifactDialog() {
final AddArtifactWindow window = new AddArtifactWindow(m_sessionDir, m_obrUrl, m_repositoryXML) {
@Override
protected ArtifactRepository getArtifactRepository() {
return m_artifactRepository;
}
@Override
protected ConnectionFactory getConnectionFactory() {
return m_connectionFactory;
}
@Override
protected LogService getLogger() {
return m_log;
}
};
// Open the subwindow by adding it to the parent window
window.showWindow(getMainWindow());
}
final void showManageResourceProcessorsDialog() {
ManageResourceProcessorWindow window = new ManageResourceProcessorWindow() {
@Override
protected ArtifactRepository getArtifactRepository() {
return m_artifactRepository;
}
};
// Open the subwindow by adding it to the parent window
window.showWindow(getMainWindow());
}
/**
* Create a new distribution in the distribution repository
*
* @param name
* the name of the new distribution;
* @param description
* the description of the new distribution.
*/
protected DistributionObject createDistribution(String name, String description) {
Map<String, String> attributes = new HashMap<>();
attributes.put(DistributionObject.KEY_NAME, name);
attributes.put(DistributionObject.KEY_DESCRIPTION, description);
Map<String, String> tags = new HashMap<>();
return m_distributionRepository.create(attributes, tags);
}
/**
* Create a new feature in the feature repository.
*
* @param name
* the name of the new feature;
* @param description
* the description of the new feature.
*/
protected FeatureObject createFeature(String name, String description) {
Map<String, String> attributes = new HashMap<>();
attributes.put(FeatureObject.KEY_NAME, name);
attributes.put(FeatureObject.KEY_DESCRIPTION, description);
Map<String, String> tags = new HashMap<>();
return m_featureRepository.create(attributes, tags);
}
/**
* Create a new target in the stateful target repository.
*
* @param name
* the name of the new target;
*/
protected StatefulTargetObject createTarget(String name) {
Map<String, String> attributes = new HashMap<>();
attributes.put(StatefulTargetObject.KEY_ID, name);
attributes.put(TargetObject.KEY_AUTO_APPROVE, "true");
Map<String, String> tags = new HashMap<>();
return m_statefulTargetRepository.preregister(attributes, tags);
}
private void addCrossPlatformAddShortcut(Button button, int keycode, String description) {
// ACE-427 - NPE when using getMainWindow() if no authentication is used...
WebApplicationContext context = (WebApplicationContext) getContext();
ShortcutHelper.addCrossPlatformShortcut(context.getBrowser(), button, description, keycode, ModifierKey.SHIFT);
}
private void addDependency(Component component, Class<?> service) {
component.add(m_manager.createServiceDependency()
.setService(service)
.setRequired(true)
);
}
private void addListener(final Object implementation, final String... topics) {
Properties props = new Properties();
props.put(EventConstants.EVENT_TOPIC, topics);
props.put(EventConstants.EVENT_FILTER, "(" + SessionFactory.SERVICE_SID + "=" + m_sessionID + ")");
Component component = m_manager.createComponent()
.setInterface(EventHandler.class.getName(), props)
.setImplementation(implementation);
synchronized (m_eventHandlers) {
m_eventHandlers.add(component);
}
m_manager.add(component);
}
private void addSessionDependency(Component component, Class<?> service) {
component.add(m_manager.createServiceDependency()
.setService(service, "(" + SessionFactory.SERVICE_SID + "=" + m_sessionID + ")")
.setRequired(true)
);
}
/**
* Determines how authentication should take place.
*/
private void authenticate() {
if (m_useAuth) {
showLoginWindow();
}
else {
// Not using authentication; use fallback scenario...
loginAutomatically();
}
}
private void cleanupListeners() {
Component[] components;
synchronized (m_eventHandlers) {
components = m_eventHandlers.toArray(new Component[m_eventHandlers.size()]);
m_eventHandlers.clear();
}
for (Component component : components) {
m_manager.remove(component);
}
}
/**
* Create a button to show a pop window for adding new features.
*
* @param user
*
* @param main
* Main Window
* @return Button
*/
private Button createAddArtifactButton() {
Button button = new Button("+");
addCrossPlatformAddShortcut(button, KeyCode.A, "Add a new artifact");
button.addListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
showAddArtifactDialog();
}
});
return button;
}
/**
* Create a button to show a popup window for adding a new distribution. On success this calls the
* createDistribution() method.
*
* @param user
*
* @return the add-distribution button instance.
*/
private Button createAddDistributionButton() {
Button button = new Button("+");
addCrossPlatformAddShortcut(button, KeyCode.D, "Add a new distribution");
button.addListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
GenericAddWindow window = new GenericAddWindow("Add Distribution") {
public void handleError(Exception e) {
// ACE-241: notify user when the distribution-creation failed!
getWindow().showNotification("Failed to add new distribution!",
"<br/>Reason: " + e.getMessage(), Notification.TYPE_ERROR_MESSAGE);
}
public void onOk(String name, String description) {
createDistribution(name, description);
}
};
window.show(getMainWindow());
}
});
return button;
}
/***
* Create a button to show popup window for adding a new feature. On success this calls the createFeature() method.
*
* @param user
*
* @return the add-feature button instance.
*/
private Button createAddFeatureButton() {
Button button = new Button("+");
addCrossPlatformAddShortcut(button, KeyCode.F, "Add a new feature");
button.addListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
GenericAddWindow window = new GenericAddWindow("Add Feature") {
public void handleError(Exception e) {
// ACE-241: notify user when the feature-creation failed!
getWindow().showNotification("Failed to add new feature!", "<br/>Reason: " + e.getMessage(),
Notification.TYPE_ERROR_MESSAGE);
}
public void onOk(String name, String description) {
createFeature(name, description);
}
};
window.show(getMainWindow());
}
});
return button;
}
/**
* Create a button to show a popup window for adding a new target. On success this calls the createTarget() method
*
* @param user
*
* @return the add-target button instance.
*/
private Button createAddTargetButton() {
Button button = new Button("+");
addCrossPlatformAddShortcut(button, KeyCode.G, "Add a new target");
button.addListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
GenericAddWindow window = new GenericAddWindow("Add Target") {
protected void handleError(Exception e) {
// ACE-241: notify user when the target-creation failed!
getWindow().showNotification("Failed to add new target!", "<br/>Reason: " + e.getMessage(),
Notification.TYPE_ERROR_MESSAGE);
}
@Override
protected void initDialog() {
m_name.setCaption("Identifier");
m_description.setVisible(false);
super.initDialog();
}
protected void onOk(String id, String description) {
createTarget(id);
}
};
window.show(getMainWindow());
}
});
return button;
}
/**
* @return a button to approve one or more targets.
*/
private Button createApproveTargetsButton() {
final Button button = new Button("A");
button.setDisableOnClick(true);
button.setImmediate(true);
button.setEnabled(false);
button.addListener(new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
m_targetsPanel.approveSelectedTargets();
}
});
m_targetsPanel.addListener(new ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
TargetsPanel targetsPanel = (TargetsPanel) event.getProperty();
Collection<?> itemIDs = (Collection<?>) targetsPanel.getValue();
boolean enabled = false;
for (Object itemID : itemIDs) {
if (targetsPanel.isItemApproveNeeded(itemID)) {
enabled = true;
break;
}
}
button.setEnabled(enabled);
}
});
return button;
}
private ArtifactsPanel createArtifactsPanel() {
return new ArtifactsPanel(m_associations, this, m_cacheRate, m_pageLength) {
@Override
protected EditWindow createEditor(final NamedObject object, final List<UIExtensionFactory> extensions) {
return new EditWindow("Edit Artifact", object, extensions) {
@Override
protected void handleError(Exception e) {
getWindow().showNotification("Failed to edit artifact!", "<br/>Reason: " + e.getMessage(),
Notification.TYPE_ERROR_MESSAGE);
}
@Override
protected void onOk(String name, String description) throws Exception {
object.setDescription(description);
}
};
}
@Override
protected ArtifactRepository getRepository() {
return m_artifactRepository;
}
@Override
protected RepositoryAdmin getRepositoryAdmin() {
return m_admin;
}
};
}
private HorizontalLayout createArtifactToolbar() {
HorizontalLayout result = new HorizontalLayout();
result.setSpacing(true);
result.addComponent(createAddArtifactButton());
result.addComponent(createManageResourceProcessorsButton());
return result;
}
private DistributionsPanel createDistributionsPanel() {
return new DistributionsPanel(m_associations, this) {
@Override
protected EditWindow createEditor(final NamedObject object, final List<UIExtensionFactory> extensions) {
return new EditWindow("Edit Distribution", object, extensions) {
@Override
protected void handleError(Exception e) {
getWindow().showNotification("Failed to edit distribution!", "<br/>Reason: " + e.getMessage(),
Notification.TYPE_ERROR_MESSAGE);
}
@Override
protected void onOk(String name, String description) throws Exception {
object.setDescription(description);
}
};
}
@Override
protected DistributionRepository getRepository() {
return m_distributionRepository;
}
@Override
protected RepositoryAdmin getRepositoryAdmin() {
return m_admin;
}
};
}
private HorizontalLayout createDistributionToolbar() {
HorizontalLayout result = new HorizontalLayout();
result.setSpacing(true);
result.addComponent(createAddDistributionButton());
return result;
}
private FeaturesPanel createFeaturesPanel() {
return new FeaturesPanel(m_associations, this) {
@Override
protected EditWindow createEditor(final NamedObject object, final List<UIExtensionFactory> extensions) {
return new EditWindow("Edit Feature", object, extensions) {
@Override
protected void handleError(Exception e) {
getWindow().showNotification("Failed to edit feature!", "<br/>Reason: " + e.getMessage(),
Notification.TYPE_ERROR_MESSAGE);
}
@Override
protected void onOk(String name, String description) throws Exception {
object.setDescription(description);
}
};
}
@Override
protected FeatureRepository getRepository() {
return m_featureRepository;
}
@Override
protected RepositoryAdmin getRepositoryAdmin() {
return m_admin;
}
};
}
private HorizontalLayout createFeatureToolbar() {
HorizontalLayout result = new HorizontalLayout();
result.setSpacing(true);
result.addComponent(createAddFeatureButton());
return result;
}
private Button createManageResourceProcessorsButton() {
// Solves ACE-224
Button button = new Button("RP");
button.addListener(new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
showManageResourceProcessorsDialog();
}
});
return button;
}
private Button createRegisterTargetsButton() {
final Button button = new Button("R");
button.setDisableOnClick(true);
button.setImmediate(true);
button.setEnabled(false);
button.addListener(new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
m_targetsPanel.registerSelectedTargets();
}
});
m_targetsPanel.addListener(new ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
TargetsPanel targetsPanel = (TargetsPanel) event.getProperty();
Collection<?> itemIDs = (Collection<?>) targetsPanel.getValue();
boolean enabled = false;
for (Object itemID : itemIDs) {
if (targetsPanel.isItemRegistrationNeeded(itemID)) {
enabled = true;
break;
}
}
button.setEnabled(enabled);
}
});
return button;
}
private TargetsPanel createTargetsPanel() {
return new TargetsPanel(m_associations, this) {
@Override
protected EditWindow createEditor(final NamedObject object, final List<UIExtensionFactory> extensions) {
return new EditWindow("Edit Target", object, extensions) {
@Override
protected void handleError(Exception e) {
getWindow().showNotification("Failed to edit target!", "<br/>Reason: " + e.getMessage(),
Notification.TYPE_ERROR_MESSAGE);
}
@Override
protected void initDialog(NamedObject object, List<UIExtensionFactory> factories) {
m_name.setCaption("Identifier");
m_name.setReadOnly(true);
m_description.setVisible(false);
super.initDialog(object, factories);
}
@Override
protected void onOk(String name, String description) throws Exception {
// Nothing to edit!
}
@Override
protected Map<String, Object> populateContext(Map<String, Object> context) {
if (object instanceof NamedTargetObject) {
context.put("statefulTarget", m_statefulTargetRepository.get(object.getDefinition()));
}
else if (object instanceof NamedStatefulTargetObject) {
context.put("statefulTarget", object.getObject());
}
return context;
}
};
}
@Override
protected TargetRepository getRepository() {
return m_targetRepository;
}
@Override
protected RepositoryAdmin getRepositoryAdmin() {
return m_admin;
}
@Override
protected StatefulTargetRepository getStatefulTargetRepository() {
return m_statefulTargetRepository;
}
};
}
private HorizontalLayout createTargetToolbar() {
HorizontalLayout result = new HorizontalLayout();
result.setSpacing(true);
result.addComponent(createAddTargetButton());
result.addComponent(createRegisterTargetsButton());
result.addComponent(createApproveTargetsButton());
return result;
}
private GridLayout createToolbar() {
return new MainActionToolbar(m_useAuth) {
@Override
protected void doAfterCommit() throws IOException {
updateTableData();
m_statusLine.setStatus("Local changes committed...");
}
@Override
protected void doAfterLogout() throws IOException {
// Close the application and reload the main window...
close();
}
@Override
protected void doAfterRetrieve() throws IOException {
updateTableData();
m_statusLine.setStatus("Repositories updated...");
}
@Override
protected void doAfterRevert() throws IOException {
updateTableData();
m_statusLine.setStatus("Local changes reverted...");
}
@Override
protected RepositoryAdmin getRepositoryAdmin() {
return m_admin;
}
@Override
protected void log(int level, String msg, Exception e, Object... args) {
m_log.log(level, String.format(msg, args), e);
}
private void updateTableData() {
m_artifactsPanel.populate();
m_featuresPanel.populate();
m_distributionsPanel.populate();
m_targetsPanel.populate();
m_mainWindow.focus();
}
};
}
/**
* Authenticates the given user by creating all dependent services.
*
* @param user
* @throws IOException
* in case of I/O problems.
*/
private boolean doLogin() {
try {
RepositoryAdminLoginContext context = m_admin.createLoginContext((User) getUser());
// @formatter:off
context
.add(context.createShopRepositoryContext()
.setLocation(m_repository).setCustomer(customerName).setName(shopRepo).setWriteable())
.add(context.createTargetRepositoryContext()
.setLocation(m_repository).setCustomer(customerName).setName(targetRepo).setWriteable())
.add(context.createDeploymentRepositoryContext()
.setLocation(m_repository).setCustomer(customerName).setName(deployRepo).setWriteable());
// @formatter:on
m_admin.login(context);
m_admin.checkout();
initGrid();
return true;
}
catch (Exception e) {
m_log.log(LogService.LOG_WARNING, "Login failed! Destroying session...", e);
try {
// Avoid errors when the user tries to login again (due to the stale session)...
m_admin.logout(true /* force */);
}
catch (IllegalStateException inner) {
// Ignore; probably we're not logged...
}
catch (IOException inner) {
m_log.log(LogService.LOG_WARNING, "Logout failed! Session possibly not destroyed...", inner);
}
return false;
}
}
private void initGrid() {
User user = (User) getUser();
Authorization auth = m_userAdmin.getAuthorization(user);
int count = 0;
for (String role : new String[] { "viewArtifact", "viewFeature", "viewDistribution", "viewTarget" }) {
if (auth.hasRole(role)) {
count++;
}
}
final GenericUploadHandler uploadHandler = new GenericUploadHandler(m_sessionDir) {
@Override
public void updateProgress(long readBytes, long contentLength) {
Float percentage = new Float(readBytes / (float) contentLength);
m_progress.setValue(percentage);
}
@Override
protected void artifactsUploaded(List<UploadHandle> uploadedArtifacts) {
StringBuilder failedMsg = new StringBuilder();
StringBuilder successMsg = new StringBuilder();
Set<String> selection = new HashSet<>();
for (UploadHandle handle : uploadedArtifacts) {
if (!handle.isSuccessful()) {
// Upload failed, so let's report this one...
appendFailure(failedMsg, handle);
m_log.log(LogService.LOG_ERROR, "Upload of " + handle.getFile() + " failed.", handle.getFailureReason());
}
else {
try {
// Upload was successful, try to upload it to our OBR...
ArtifactObject artifact = uploadToOBR(handle);
if (artifact != null) {
selection.add(artifact.getDefinition());
appendSuccess(successMsg, handle);
}
}
catch (ArtifactAlreadyExistsException exception) {
appendFailureExists(failedMsg, handle);
m_log.log(LogService.LOG_WARNING, "Upload of " + handle.getFilename() + " failed, as it already exists!");
}
catch (Exception exception) {
appendFailure(failedMsg, handle, exception);
m_log.log(LogService.LOG_ERROR, "Upload of " + handle.getFilename() + " failed.", exception);
}
}
// We're done with this (temporary) file, so we can remove it...
handle.cleanup();
}
m_artifactsPanel.setValue(selection);
// Notify the user what the overall status was...
Notification notification = createNotification(failedMsg, successMsg);
getMainWindow().showNotification(notification);
m_progress.setStyleName("invisible");
m_statusLine.setStatus(notification.getCaption() + "...");
}
@Override
protected void uploadStarted(UploadHandle upload) {
m_progress.setStyleName("visible");
m_progress.setValue(new Float(0.0f));
m_statusLine.setStatus("Upload of '%s' started...", upload.getFilename());
}
private void appendFailure(StringBuilder sb, UploadHandle handle) {
appendFailure(sb, handle, handle.getFailureReason());
}
private void appendFailure(StringBuilder sb, UploadHandle handle, Exception cause) {
sb.append("<li>'").append(handle.getFile().getName()).append("': failed");
if (cause != null) {
sb.append(", possible reason:<br/>").append(cause.getMessage());
}
sb.append("</li>");
}
private void appendFailureExists(StringBuilder sb, UploadHandle handle) {
sb.append("<li>'").append(handle.getFile().getName()).append("': already exists in repository</li>");
}
private void appendSuccess(StringBuilder sb, UploadHandle handle) {
sb.append("<li>'").append(handle.getFile().getName()).append("': added to repository</li>");
}
private Notification createNotification(StringBuilder failedMsg, StringBuilder successMsg) {
String caption = "Upload completed";
int delay = 500; // msec.
StringBuilder notification = new StringBuilder();
if (failedMsg.length() > 0) {
caption = "Upload completed with failures";
delay = -1;
notification.append("<ul>").append(failedMsg).append("</ul>");
}
if (successMsg.length() > 0) {
notification.append("<ul>").append(successMsg).append("</ul>");
}
if (delay < 0) {
notification.append("<p>(click to dismiss this notification).</p>");
}
Notification summary = new Notification(caption, notification.toString(), Notification.TYPE_TRAY_NOTIFICATION);
summary.setDelayMsec(delay);
return summary;
}
private ArtifactObject uploadToOBR(UploadHandle handle) throws IOException {
return UploadHelper.importRemoteBundle(m_artifactRepository, handle.getFile());
}
};
m_grid = new GridLayout(count, 4);
m_grid.setSpacing(true);
m_grid.setSizeFull();
m_mainToolbar = createToolbar();
m_grid.addComponent(m_mainToolbar, 0, 0, count - 1, 0);
m_artifactsPanel = createArtifactsPanel();
m_artifactToolbar = createArtifactToolbar();
final DragAndDropWrapper artifactsPanelWrapper = new DragAndDropWrapper(m_artifactsPanel);
artifactsPanelWrapper.setDragStartMode(DragStartMode.HTML5);
artifactsPanelWrapper.setDropHandler(new ArtifactDropHandler(uploadHandler));
artifactsPanelWrapper.setCaption(m_artifactsPanel.getCaption());
artifactsPanelWrapper.setSizeFull();
count = 0;
if (auth.hasRole("viewArtifact")) {
m_grid.addComponent(artifactsPanelWrapper, count, 2);
m_grid.addComponent(m_artifactToolbar, count, 1);
count++;
}
m_featuresPanel = createFeaturesPanel();
m_featureToolbar = createFeatureToolbar();
if (auth.hasRole("viewFeature")) {
m_grid.addComponent(m_featuresPanel, count, 2);
m_grid.addComponent(m_featureToolbar, count, 1);
count++;
}
m_distributionsPanel = createDistributionsPanel();
m_distributionToolbar = createDistributionToolbar();
if (auth.hasRole("viewDistribution")) {
m_grid.addComponent(m_distributionsPanel, count, 2);
m_grid.addComponent(m_distributionToolbar, count, 1);
count++;
}
m_targetsPanel = createTargetsPanel();
m_targetToolbar = createTargetToolbar();
if (auth.hasRole("viewTarget")) {
m_grid.addComponent(m_targetsPanel, count, 2);
m_grid.addComponent(m_targetToolbar, count, 1);
}
m_statusLine = new StatusLine();
m_grid.addComponent(m_statusLine, 0, 3, 2, 3);
m_progress = new ProgressIndicator(0f);
m_progress.setStyleName("invisible");
m_progress.setIndeterminate(false);
m_progress.setPollingInterval(1000);
m_grid.addComponent(m_progress, 3, 3);
m_grid.setRowExpandRatio(2, 1.0f);
m_grid.setColumnExpandRatio(0, 0.31f);
m_grid.setColumnExpandRatio(1, 0.23f);
m_grid.setColumnExpandRatio(2, 0.23f);
m_grid.setColumnExpandRatio(3, 0.23f);
// Wire up all panels so they have the correct associations...
m_artifactsPanel.setAssociatedTables(null, m_featuresPanel);
m_featuresPanel.setAssociatedTables(m_artifactsPanel, m_distributionsPanel);
m_distributionsPanel.setAssociatedTables(m_featuresPanel, m_targetsPanel);
m_targetsPanel.setAssociatedTables(m_distributionsPanel, null);
addListener(m_statusLine, StatefulTargetObject.TOPIC_ALL, RepositoryObject.PUBLIC_TOPIC_ROOT.concat(RepositoryObject.TOPIC_ALL_SUFFIX));
addListener(m_mainToolbar, StatefulTargetObject.TOPIC_ALL, RepositoryAdmin.TOPIC_STATUSCHANGED, RepositoryAdmin.TOPIC_LOGIN, RepositoryAdmin.TOPIC_REFRESH);
addListener(m_artifactsPanel, ArtifactObject.TOPIC_ALL, RepositoryAdmin.TOPIC_STATUSCHANGED, RepositoryAdmin.TOPIC_LOGIN, RepositoryAdmin.TOPIC_REFRESH);
addListener(m_featuresPanel, FeatureObject.TOPIC_ALL, RepositoryAdmin.TOPIC_STATUSCHANGED, RepositoryAdmin.TOPIC_LOGIN, RepositoryAdmin.TOPIC_REFRESH);
addListener(m_distributionsPanel, DistributionObject.TOPIC_ALL, RepositoryAdmin.TOPIC_STATUSCHANGED, RepositoryAdmin.TOPIC_LOGIN, RepositoryAdmin.TOPIC_REFRESH);
addListener(m_targetsPanel, StatefulTargetObject.TOPIC_ALL, TargetObject.TOPIC_ALL, RepositoryAdmin.TOPIC_STATUSCHANGED, RepositoryAdmin.TOPIC_LOGIN, RepositoryAdmin.TOPIC_REFRESH);
m_mainWindow.addComponent(m_grid);
// Ensure the focus is properly defined (for the shortcut keys to work)...
m_mainWindow.focus();
}
/**
* @return <code>true</code> if the login succeeded, <code>false</code> otherwise.
*/
private boolean loginAutomatically() {
setUser(m_userAdmin.getUser("username", m_userName));
return doLogin();
}
/**
* Shows the login window on the center of the main window.
*/
private void showLoginWindow() {
LoginWindow loginWindow = new LoginWindow(m_log, this);
loginWindow.openWindow(getMainWindow());
}
}