Java tutorial
/* * 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. */ @SuppressWarnings("serial") 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; } 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); initGrid(); m_admin.checkout(); 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()); } }