/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI 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.openengsb.ui.admin.testClient; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton; import org.apache.wicket.extensions.markup.html.tree.BaseTree; import org.apache.wicket.extensions.markup.html.tree.LinkTree; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.markup.repeater.RepeatingView; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.openengsb.connector.usernamepassword.Password; import org.openengsb.core.api.ConnectorManager; import org.openengsb.core.api.ConnectorProvider; import org.openengsb.core.api.Constants; import org.openengsb.core.api.Domain; import org.openengsb.core.api.DomainProvider; import org.openengsb.core.api.OsgiServiceNotAvailableException; import org.openengsb.core.api.OsgiUtilsService; import org.openengsb.core.api.WiringService; import org.openengsb.core.api.context.ContextHolder; import org.openengsb.core.api.descriptor.ServiceDescriptor; import org.openengsb.core.api.model.BeanDescription; import org.openengsb.core.api.persistence.PersistenceException; import org.openengsb.core.api.remote.MethodCallMessage; import org.openengsb.core.api.security.annotation.SecurityAttribute; import org.openengsb.core.api.security.annotation.SecurityAttributes; import org.openengsb.core.api.security.model.SecurityAttributeEntry; import org.openengsb.core.common.SecurityAttributeProviderImpl; import org.openengsb.core.util.Comparators; import org.openengsb.core.util.JsonUtils; import org.openengsb.ui.admin.basePage.BasePage; import org.openengsb.ui.admin.connectorEditorPage.ConnectorEditorPage; import org.openengsb.ui.admin.methodArgumentPanel.MethodArgumentPanel; import org.openengsb.ui.admin.model.Argument; import org.openengsb.ui.admin.model.ArgumentConversionException; import org.openengsb.ui.admin.model.MethodCall; import org.openengsb.ui.admin.model.MethodId; import org.openengsb.ui.admin.model.ServiceId; import org.openengsb.ui.admin.organizeGlobalsPage.OrganizeGlobalsPage; import org.openengsb.ui.admin.organizeImportsPage.OrganizeImportsPage; import org.openengsb.ui.admin.util.MethodComparator; import org.openengsb.ui.common.model.LocalizableStringModel; import org.ops4j.pax.wicket.api.PaxWicketMountPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.SerializationFeature; import com.google.common.base.Function; import com.google.common.collect.Collections2; @SecurityAttributes({ @SecurityAttribute(key = "org.openengsb.ui.component", value = "SERVICE_USER"), @SecurityAttribute(key = "org.openengsb.ui.component", value = "SERVICE_EDITOR") }) @PaxWicketMountPoint(mountPoint = "tester") public class TestClient extends BasePage { private static final long serialVersionUID = 2993665629913347770L; private static final Logger LOGGER = LoggerFactory.getLogger(TestClient.class); public static final String PAGE_NAME_KEY = "testClient.title"; public static final String PAGE_DESCRIPTION_KEY = "testClient.description"; @Inject @Named("wiringService") private WiringService wiringService; @Inject @Named("osgiUtilsService") private OsgiUtilsService utilsService; @Inject @Named("serviceManager") private ConnectorManager serviceManager; @Inject @Named("attributeStore") private SecurityAttributeProviderImpl attributeStore; private DropDownChoice<MethodId> methodList; private final MethodCall call = new MethodCall(); private RepeatingView argumentList; private WebMarkupContainer argumentListContainer; private LinkTree serviceList; private FeedbackPanel feedbackPanel; private AjaxButton editButton; private AjaxButton deleteButton; private AjaxButton submitButton; private AjaxButton jsonButton; @SuppressWarnings("serial") private final IModel<? extends List<? extends DomainProvider>> domainProvider = new LoadableDetachableModel<List<? extends DomainProvider>>() { @Override protected List<? extends DomainProvider> load() { List<DomainProvider> serviceList = utilsService.listServices(DomainProvider.class); Collections.sort(serviceList, Comparators.forDomainProvider()); return serviceList; } }; public TestClient() { super(); initContent(); } public TestClient(PageParameters parameters) { super(parameters, PAGE_NAME_KEY); initContent(); } private void initContent() { WebMarkupContainer serviceManagementContainer = new WebMarkupContainer("serviceManagementContainer"); serviceManagementContainer.setOutputMarkupId(true); add(serviceManagementContainer); attributeStore.putAttribute(serviceManagementContainer, new SecurityAttributeEntry( "org.openengsb.ui.component", "SERVICE_EDITOR")); serviceManagementContainer.add(makeServiceList()); Form<Object> organize = createOrganizeForm(); add(organize); Form<MethodCall> form = createMethodCallForm(); add(form); feedbackPanel = new FeedbackPanel("feedback"); feedbackPanel.setOutputMarkupId(true); add(feedbackPanel); Form<?> pc = new Form<Object>("projectChoiceForm"); pc.add(createProjectChoice()); add(pc); } //TODO: OPENENGSB-3272: Extract this into an own component private Component createProjectChoice() { DropDownChoice<String> dropDownChoice = new DropDownChoice<String>("projectChoice", new IModel<String>() { @Override public String getObject() { return getSessionContextId(); } @Override public void setObject(String object) { ContextHolder.get().setCurrentContextId(object); } @Override public void detach() { } }, getAvailableContexts()) { /** * */ private static final long serialVersionUID = 1L; @Override protected boolean wantOnSelectionChangedNotifications() { return true; } @Override protected void onModelChanged() { setResponsePage(TestClient.this.getClass()); } }; return dropDownChoice; } @SuppressWarnings("serial") private Form<MethodCall> createMethodCallForm() { Form<MethodCall> form = new Form<MethodCall>("methodCallForm"); form.setModel(new Model<MethodCall>(call)); form.setOutputMarkupId(true); editButton = initializeEditButton(form); editButton.setEnabled(false); editButton.setOutputMarkupId(true); deleteButton = initializeDeleteButton(form); deleteButton.setEnabled(false); deleteButton.setOutputMarkupId(true); methodList = new DropDownChoice<MethodId>("methodList"); methodList.setModel(new PropertyModel<MethodId>(call, "method")); methodList.setChoiceRenderer(new ChoiceRenderer<MethodId>()); methodList.setOutputMarkupId(true); methodList.add(new AjaxFormComponentUpdatingBehavior("onchange") { @Override protected void onUpdate(AjaxRequestTarget target) { LOGGER.info("method selected: " + call.getMethod()); populateArgumentList(); target.add(argumentListContainer); } }); form.add(methodList); argumentListContainer = new WebMarkupContainer("argumentListContainer"); argumentListContainer.setOutputMarkupId(true); argumentList = new RepeatingView("argumentList"); argumentList.setOutputMarkupId(true); argumentListContainer.add(argumentList); form.add(argumentListContainer); submitButton = initializeSubmitButton(form); jsonButton = initializeJsonButton(form); serviceList = new LinkTree("serviceList", createModel()) { @Override protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target) { DefaultMutableTreeNode mnode = (DefaultMutableTreeNode) node; try { argumentList.removeAll(); target.add(argumentListContainer); ServiceId service = (ServiceId) mnode.getUserObject(); LOGGER.info("clicked on node {} of type {}", node, node.getClass()); call.setService(service); populateMethodList(); updateModifyButtons(service); jsonButton.setEnabled(true); } catch (ClassCastException ex) { LOGGER.info("clicked on not ServiceId node"); methodList.setChoices(new ArrayList<MethodId>()); editButton.setEnabled(false); deleteButton.setEnabled(false); submitButton.setEnabled(false); jsonButton.setEnabled(false); } target.add(methodList); target.add(editButton); target.add(deleteButton); target.add(submitButton); target.add(jsonButton); target.add(feedbackPanel); } }; serviceList.setOutputMarkupId(true); form.add(serviceList); serviceList.getTreeState().expandAll(); submitButton.setOutputMarkupId(true); submitButton.setEnabled(false); jsonButton.setOutputMarkupId(true); jsonButton.setEnabled(false); form.add(submitButton); form.add(editButton); form.add(deleteButton); form.add(jsonButton); return form; } @SuppressWarnings("serial") private AjaxButton initializeEditButton(Form<MethodCall> form) { return new AjaxButton("editButton", form) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { LOGGER.info("edit button pressed"); String serviceId = call.getService().getServiceId(); setResponsePage(new ConnectorEditorPage(serviceId)); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { LOGGER.warn("Submit error during editButton."); } }; } @SuppressWarnings("serial") private AjaxButton initializeDeleteButton(Form<MethodCall> form) { return new AjaxButton("deleteButton", form) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { LOGGER.info("delete button pressed"); String serviceId = call.getService().getServiceId(); try { serviceManager.delete(serviceId); info("service " + serviceId + " successfully deleted"); serviceList.setModelObject(createModel()); serviceList.getTreeState().expandAll(); target.add(serviceList); } catch (PersistenceException e) { error("Unable to delete Service due to: " + e.getLocalizedMessage()); } target.add(feedbackPanel); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { LOGGER.warn("Submit error during editButton."); } }; } @SuppressWarnings("serial") private IndicatingAjaxButton initializeSubmitButton(Form<MethodCall> form) { return new IndicatingAjaxButton("submitButton", form) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { target.add(feedbackPanel); performCall(); call.getArguments().clear(); argumentList.removeAll(); call.setMethod(null); populateMethodList(); target.add(methodList); target.add(argumentListContainer); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { LOGGER.warn("Error during submitting with submitButton"); } }; } @SuppressWarnings("serial") private IndicatingAjaxButton initializeJsonButton(Form<MethodCall> form) { return new IndicatingAjaxButton("jsonButton", form) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { target.add(feedbackPanel); displayJSONMessages(); call.getArguments().clear(); argumentList.removeAll(); call.setMethod(null); populateMethodList(); target.add(methodList); target.add(argumentListContainer); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { LOGGER.warn("Error during submissiong with jsonButton"); } }; } /** * creates the form for organize section (globals, imports) */ private Form<Object> createOrganizeForm() { Form<Object> organize = new Form<Object>("organizeForm"); organize.setOutputMarkupId(true); @SuppressWarnings("serial") AjaxButton globalsButton = new AjaxButton("globalsButton", organize) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { setResponsePage(OrganizeGlobalsPage.class); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { LOGGER.warn("Error during submit of globalButton ajax link"); } }; globalsButton.setOutputMarkupId(true); organize.add(globalsButton); @SuppressWarnings("serial") AjaxButton importsButton = new AjaxButton("importsButton", organize) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { setResponsePage(OrganizeImportsPage.class); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { LOGGER.warn("Error during submit of importsButton page"); } }; importsButton.setOutputMarkupId(true); organize.add(importsButton); return organize; } @SuppressWarnings("serial") private ListView<DomainProvider> makeServiceList() { return new ListView<DomainProvider>("domains", domainProvider) { @Override protected void populateItem(final ListItem<DomainProvider> item) { final String domainType = item.getModelObject().getId(); item.add(new Label("domain.name", new LocalizableStringModel(this, item.getModelObject().getName()))); item.add(new Link<DomainProvider>("proxy.create.new", item.getModel()) { @Override public void onClick() { setResponsePage(new ConnectorEditorPage(getModelObject().getId(), Constants.EXTERNAL_CONNECTOR_PROXY)); } }); item.add(new Label("domain.description", new LocalizableStringModel(this, item.getModelObject() .getDescription()))); item.add(new Label("domain.class", item.getModelObject().getDomainInterface().getName())); IModel<? extends List<? extends ConnectorProvider>> connectorProviderModel = new LoadableDetachableModel<List<? extends ConnectorProvider>>() { @Override protected List<? extends ConnectorProvider> load() { return utilsService.listServices(ConnectorProvider.class, String.format("(%s=%s)", Constants.DOMAIN_KEY, domainType)); } }; item.add(new ListView<ConnectorProvider>("services", connectorProviderModel) { @Override protected void populateItem(ListItem<ConnectorProvider> item) { ServiceDescriptor desc = item.getModelObject().getDescriptor(); item.add(new Link<ConnectorProvider>("create.new", item.getModel()) { @Override public void onClick() { setResponsePage(new ConnectorEditorPage(domainType, getModelObject().getId())); } }); item.add(new Label("service.name", new LocalizableStringModel(this, desc.getName()))); item.add(new Label("service.description", new LocalizableStringModel(this, desc .getDescription()))); } }); } }; } /** * Returns the ID of the currently selected Service or null if none was selected * * @return the ID of the currently selected Service or null if none was selected */ private ServiceId fetchCurrentSelectService() { return call.getService(); } /** * Returns the ID of the currently selected Method or null if none was selected * * @return the ID of the currently selected Method or null if none was selected */ private MethodId fetchCurrentSelectMethod() { return call.getMethod(); } /** * Returns a Standard MethodCall with of the selected Method * * @param methodId Id of the refered Method * @return a Standard MethodCall with of the selected Method */ private org.openengsb.core.api.remote.MethodCall createRealMethodCall(MethodId methodId) throws ArgumentConversionException { Class<?>[] classes = methodId.getArgumentTypes(); List<String> classList = new ArrayList<String>(); for (Class<?> clazz : classes) { classList.add(clazz.getName()); } return new org.openengsb.core.api.remote.MethodCall(methodId.getName(), call.getArgumentsAsArray(), classList); } /** * Creates a MethodCall and wraps the it in a MethodCallRequest with addiontal MetaData.<br/> * Returns this MethodCallRequest. * * @param serviceId Id of the refered Service * @param methodId Id of the refered Method * @return a MethodCallRequest with MetaData corresponding to the given ServiceId and MethodId */ private MethodCallMessage createMethodCallRequest(ServiceId serviceId, MethodId methodId) throws ArgumentConversionException { org.openengsb.core.api.remote.MethodCall realMethodCall = createRealMethodCall(methodId); realMethodCall.setMetaData(createMetaDataForMethodCallRequest(serviceId)); return new MethodCallMessage(realMethodCall, "randomCallId"); } /** * Creates a MethodCallRequest and wraps it in a SecureRequest, this adds the authentication block to the Message * Returns this SecureRequest. * * @param serviceId Id of the refered Service * @param methodId Id of the refered Method * @return a SecureRequest corresponding to the given ServiceId and MethodId */ private MethodCallMessage createSecureRequest(ServiceId serviceId, MethodId methodId) throws ArgumentConversionException { MethodCallMessage methodCallRequest = createMethodCallRequest(serviceId, methodId); BeanDescription beanDescription = BeanDescription.fromObject(new Password("yourpassword")); methodCallRequest.setPrincipal("yourusername"); methodCallRequest.setCredentials(beanDescription); return methodCallRequest; } /** * create nessecary MetaData for the Json Message * * @param serviceId to fetch the context Data of the message * @return a Map with the nessecary MetaData for the Message */ private Map<String, String> createMetaDataForMethodCallRequest(ServiceId serviceId) { Map<String, String> metaData = new HashMap<String, String>(); if (serviceId.getServiceId() == null) { metaData.put("serviceId", serviceId.getDomainName()); } else { metaData.put("serviceId", serviceId.getServiceId()); } metaData.put("contextId", getSessionContextId()); return metaData; } /** * Returns the constructed SecureRequest, via an ObjectMapper, as a JsonMessage String * * @param secureRequest the request to parse to a JsonString * @return the constructed SecureRequest, via an ObjectMapper, as a JsonMessage String */ private String parseRequestToJsonString(MethodCallMessage secureRequest) { String jsonResult = ""; try { jsonResult = JsonUtils.createObjectMapperWithIntroSpectors() .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) .writeValueAsString(secureRequest); } catch (IOException ex) { handleExceptionWithFeedback(ex); jsonResult = ""; } return jsonResult; } /** * filter (unwanted) metaData entries from the args list, this is a dirty hack and should be replaced if possible. * TODO [Openengsb 1411] replace this with stable filter mechanism * * @param jsonMessage Message to filter * @return the jsonMessage filtered from the unnessecary data */ private String filterUnnessecaryArgumentsFromJsonMessage(String jsonMessage) { String typeToReplace = ",\"type\":"; while (jsonMessage.contains(typeToReplace)) { int posAfterType = jsonMessage.indexOf(typeToReplace) + typeToReplace.length(); String firstPart = jsonMessage.substring(0, jsonMessage.indexOf(typeToReplace)); String lastPart = jsonMessage.substring(posAfterType, jsonMessage.length()); int endOfArgs = lastPart.indexOf("}]"); int firstSemicolon = lastPart.indexOf(","); if (firstSemicolon < endOfArgs) { lastPart = lastPart.substring(lastPart.indexOf(","), lastPart.length()); } else { lastPart = lastPart.substring(lastPart.indexOf("}]"), lastPart.length()); } jsonMessage = firstPart + lastPart; } jsonMessage = jsonMessage.replaceAll(",\"processId\":null,\"origin\":null", ""); return jsonMessage; } /** * Displays the corresponding message to the currently selected Method of the currently active Service in the * "ServiceTree" */ private void displayJSONMessages() { ServiceId serviceId = fetchCurrentSelectService(); MethodId methodId = fetchCurrentSelectMethod(); if (serviceId == null) { String serviceNotSet = new StringResourceModel("json.view.ServiceNotSet", this, null).getString(); info(serviceNotSet); return; } if (methodId == null) { String methodNotSet = new StringResourceModel("json.view.MethodNotSet", this, null).getString(); info(methodNotSet); return; } try { String jsonResult = parseRequestToJsonString(createSecureRequest(serviceId, methodId)); String jsonPrefix = new StringResourceModel("json.view.MessagePrefix", this, null).getString(); jsonResult = filterUnnessecaryArgumentsFromJsonMessage(jsonResult); info(String.format("%s %s", jsonPrefix, jsonResult)); } catch (ArgumentConversionException e) { printArgumentConversionException(e); } } public TestClient(ServiceId jumpToService) { this(); serviceList.getTreeState().collapseAll(); TreeModel treeModel = serviceList.getModelObject(); DefaultMutableTreeNode serviceNode = findService((DefaultMutableTreeNode) treeModel.getRoot(), jumpToService); expandAllUntilChild(serviceNode); serviceList.getTreeState().selectNode(serviceNode, true); call.setService(jumpToService); populateMethodList(); } private void expandAllUntilChild(DefaultMutableTreeNode child) { for (TreeNode n : child.getPath()) { serviceList.getTreeState().expandNode(n); } } private DefaultMutableTreeNode findService(DefaultMutableTreeNode node, ServiceId jumpToService) { if (node.isLeaf()) { Object userObject = node.getUserObject(); if (jumpToService.equals(userObject)) { return node; } } else { for (int i = 0; i < node.getChildCount(); i++) { DefaultMutableTreeNode result = findService((DefaultMutableTreeNode) node.getChildAt(i), jumpToService); if (result != null) { return result; } } } return null; } private void updateModifyButtons(ServiceId serviceId) { editButton.setEnabled(false); editButton.setEnabled(serviceId.getServiceId() != null); deleteButton.setEnabled(false); deleteButton.setEnabled(serviceId.getServiceId() != null); } private TreeModel createModel() { DefaultMutableTreeNode node = new DefaultMutableTreeNode("Select Instance"); TreeModel model = new DefaultTreeModel(node); LOGGER.info("adding domains"); List<? extends DomainProvider> providerList = domainProvider.getObject(); Collections.sort(providerList, Comparators.forDomainProvider()); for (DomainProvider provider : providerList) { LOGGER.info("adding " + provider.getName()); addDomainProvider(provider, node); } LOGGER.info("done adding domains;"); return model; } private void addDomainProvider(DomainProvider provider, DefaultMutableTreeNode node) { String providerName = provider.getName().getString(getSession().getLocale()); DefaultMutableTreeNode providerNode = new DefaultMutableTreeNode(providerName); node.add(providerNode); // add domain entry to call via domain endpoint factory ServiceId domainProviderServiceId = new ServiceId(); Class<? extends Domain> domainInterface = provider.getDomainInterface(); domainProviderServiceId.setServiceClass(domainInterface); domainProviderServiceId.setDomainName(provider.getId()); DefaultMutableTreeNode endPointReferenceNode = new DefaultMutableTreeNode(domainProviderServiceId, false); providerNode.add(endPointReferenceNode); // add all corresponding services List<? extends Domain> domainEndpoints = wiringService.getDomainEndpoints(domainInterface, "*"); for (Domain serviceReference : domainEndpoints) { String id = serviceReference.getInstanceId(); if (id != null) { ServiceId serviceId = new ServiceId(); serviceId.setServiceId(id); serviceId.setServiceClass(domainInterface); DefaultMutableTreeNode referenceNode = new DefaultMutableTreeNode(serviceId, false); providerNode.add(referenceNode); } } } private Method getMethodOfService(Object service, MethodId methodId) throws NoSuchMethodException { if (methodId == null) { String string = new StringResourceModel("serviceError", this, null).getString(); error(string); return null; } return service.getClass().getMethod(methodId.getName(), methodId.getArgumentTypes()); } protected void performCall() { Object service; try { service = getService(call.getService()); } catch (OsgiServiceNotAvailableException e1) { handleExceptionWithFeedback(e1); return; } Method method; try { method = getMethodOfService(service, call.getMethod()); } catch (NoSuchMethodException ex) { throw new IllegalArgumentException(ex); } if (method == null) { return; } try { Object result = method.invoke(service, call.getArgumentsAsArray()); info("Methodcall called successfully"); Class<?> returnType = method.getReturnType(); if (returnType.equals(void.class)) { return; } String resultString; if (returnType.isArray()) { try { // to handle byte[] and char[] Constructor<String> constructor = String.class.getConstructor(returnType); resultString = constructor.newInstance(result); } catch (Exception e) { resultString = ArrayUtils.toString(result); } } else { resultString = result.toString(); } info("Result " + returnType.getName() + ": " + resultString); LOGGER.info("result: {}", resultString); } catch (IllegalAccessException e) { handleExceptionWithFeedback(e); } catch (InvocationTargetException e) { handleExceptionWithFeedback(e.getCause()); } catch (ArgumentConversionException e) { printArgumentConversionException(e); } } private void printArgumentConversionException(ArgumentConversionException e) { Argument argument = e.getArgument(); String error = new StringResourceModel("conversion.error", this, null).getString(); error = String.format(error, argument.getIndex(), argument.getType().getName()); error(error); error(ExceptionUtils.getFullStackTrace(e)); LOGGER.error(error, e); } protected void populateArgumentList() { argumentList.removeAll(); ServiceId service = call.getService(); Object serviceObject; try { if (service.getDomainName() != null) { serviceObject = getServiceViaDomainEndpointFactory(service); } else { serviceObject = getService(service); } } catch (OsgiServiceNotAvailableException e) { handleExceptionWithFeedback(e); return; } if (call.getMethod() == null) { return; } Method m = findMethod(serviceObject.getClass(), call.getMethod()); List<Argument> arguments = new ArrayList<Argument>(); call.setArguments(arguments); int i = 0; for (Class<?> p : m.getParameterTypes()) { Argument argModel = new Argument(i + 1, p, null); arguments.add(argModel); MethodArgumentPanel argumentPanel = new MethodArgumentPanel("arg" + i + "panel", argModel); argumentList.add(argumentPanel); i++; } call.setArguments(arguments); } private void handleExceptionWithFeedback(Throwable e) { String stackTrace = ExceptionUtils.getFullStackTrace(e); error(stackTrace); LOGGER.error(e.getMessage(), e); } private void populateMethodList() { ServiceId service = call.getService(); List<Method> methods = getServiceMethods(service); Collection<String> methodSignatures = Collections2.transform(methods, new Function<Method, String>() { @Override public String apply(Method input) { Class<?>[] parameterTypes = input.getParameterTypes(); String[] parameterTypeNames = new String[parameterTypes.length]; for (int i = 0; i < parameterTypeNames.length; i++) { parameterTypeNames[i] = parameterTypes[i].getSimpleName(); } return input.getName() + "(" + StringUtils.join(parameterTypeNames, ", ") + ")"; } }); LOGGER.info("found {} methods: {}", methods.size()); for (String s : methodSignatures) { LOGGER.info("# " + s); } List<MethodId> methodChoices = new ArrayList<MethodId>(); for (Method m : methods) { methodChoices.add(new MethodId(m)); } methodList.setChoices(methodChoices); LOGGER.info("populating list with: {}", methodChoices); } @SuppressWarnings("unchecked") private List<Method> getServiceMethods(ServiceId service) { if (service == null) { return Collections.emptyList(); } Class<?> connectorInterface = service.getServiceClass(); if (wiringService.isConnectorCurrentlyPresent((Class<? extends Domain>) connectorInterface)) { submitButton.setEnabled(true); List<Method> result = Arrays.asList(connectorInterface.getMethods()); Collections.sort(result, new MethodComparator()); return result; } error("No service found for domain: " + connectorInterface.getName()); submitButton.setEnabled(false); return new ArrayList<Method>(); } private Object getService(ServiceId service) throws OsgiServiceNotAvailableException { String serviceId = service.getServiceId(); if (serviceId != null) { return utilsService.getServiceWithId(service.getServiceClass(), serviceId); } else { String domainName = service.getDomainName(); String location = "domain/" + domainName + "/default"; Class<?> serviceClazz = service.getServiceClass(); return utilsService.getServiceForLocation(serviceClazz, location); } } @SuppressWarnings("unchecked") private Object getServiceViaDomainEndpointFactory(ServiceId service) { String name = service.getDomainName(); Class<? extends Domain> aClass; aClass = (Class<? extends Domain>) service.getServiceClass(); if (wiringService.isConnectorCurrentlyPresent(aClass)) { return wiringService.getDomainEndpoint(aClass, "domain/" + name + "/default"); } throw new OsgiServiceNotAvailableException("no default service found for service: " + service.getServiceClass()); } private Method findMethod(Class<?> serviceClass, MethodId methodId) { try { return serviceClass.getMethod(methodId.getName(), methodId.getArgumentTypes()); } catch (SecurityException e) { throw new IllegalStateException(e); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e); } } }