| // Licensed 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.tapestry5.integration.app1.services; |
| |
| import static java.lang.annotation.ElementType.FIELD; |
| import static java.lang.annotation.ElementType.PARAMETER; |
| import static java.lang.annotation.RetentionPolicy.RUNTIME; |
| |
| import java.io.IOException; |
| import java.lang.annotation.Documented; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.Target; |
| import java.net.URL; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.tapestry5.SymbolConstants; |
| import org.apache.tapestry5.ValueEncoder; |
| import org.apache.tapestry5.integration.app1.data.Address; |
| import org.apache.tapestry5.integration.app1.data.Entity; |
| import org.apache.tapestry5.integration.app1.data.ToDoItem; |
| import org.apache.tapestry5.integration.app1.data.Track; |
| import org.apache.tapestry5.internal.services.GenericValueEncoderFactory; |
| import org.apache.tapestry5.ioc.Configuration; |
| import org.apache.tapestry5.ioc.MappedConfiguration; |
| import org.apache.tapestry5.ioc.OrderedConfiguration; |
| import org.apache.tapestry5.ioc.Resource; |
| import org.apache.tapestry5.ioc.ServiceBinder; |
| import org.apache.tapestry5.ioc.annotations.Contribute; |
| import org.apache.tapestry5.ioc.annotations.ImportModule; |
| import org.apache.tapestry5.ioc.annotations.Marker; |
| import org.apache.tapestry5.ioc.annotations.Value; |
| import org.apache.tapestry5.ioc.internal.util.CollectionFactory; |
| import org.apache.tapestry5.ioc.services.ServiceOverride; |
| import org.apache.tapestry5.modules.Bootstrap4Module; |
| import org.apache.tapestry5.modules.NoBootstrapModule; |
| import org.apache.tapestry5.services.BaseURLSource; |
| import org.apache.tapestry5.services.BeanBlockContribution; |
| import org.apache.tapestry5.services.BeanBlockSource; |
| import org.apache.tapestry5.services.ComponentClassResolver; |
| import org.apache.tapestry5.services.EditBlockContribution; |
| import org.apache.tapestry5.services.LibraryMapping; |
| import org.apache.tapestry5.services.Request; |
| import org.apache.tapestry5.services.RequestFilter; |
| import org.apache.tapestry5.services.RequestHandler; |
| import org.apache.tapestry5.services.ResourceDigestGenerator; |
| import org.apache.tapestry5.services.Response; |
| import org.apache.tapestry5.services.ValueEncoderFactory; |
| import org.apache.tapestry5.services.ValueLabelProvider; |
| import org.apache.tapestry5.services.compatibility.Compatibility; |
| import org.apache.tapestry5.services.compatibility.Trait; |
| import org.apache.tapestry5.services.pageload.PagePreloader; |
| import org.apache.tapestry5.services.pageload.PreloaderMode; |
| import org.apache.tapestry5.services.security.ClientWhitelist; |
| import org.apache.tapestry5.services.security.WhitelistAnalyzer; |
| import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2; |
| import org.slf4j.Logger; |
| |
| /** |
| * I was just dying to see how fast requests are! |
| */ |
| //@ImportModule(Bootstrap4Module.class) |
| //@ImportModule(NoBootstrapModule.class) |
| public class AppModule |
| { |
| |
| final public static String D3_URL_SYMBOL = "d3.url"; |
| |
| /** |
| * Used to disambiguate services in this module from services in other modules that share the |
| * same service |
| * interface. |
| */ |
| @Target( |
| {PARAMETER, FIELD}) |
| @Retention(RUNTIME) |
| @Documented |
| public @interface Local |
| { |
| |
| } |
| |
| public static void bind(ServiceBinder binder) |
| { |
| binder.bind(Reloadable.class); |
| binder.bind(MessageAccess.class); |
| } |
| |
| public static void contributeValidatorMacro(MappedConfiguration<String, String> configuration) |
| { |
| configuration.add("password", "required,lengthBetweenTwoAndThree"); |
| configuration.add("lengthBetweenTwoAndThree", "minlength=2,maxlength=3"); |
| } |
| |
| @Contribute(ServiceOverride.class) |
| public void setupCustomBaseURLSource(MappedConfiguration<Class, Object> configuration) |
| { |
| BaseURLSource source = new BaseURLSource() |
| { |
| public String getBaseURL(boolean secure) |
| { |
| String protocol = secure ? "https" : "http"; |
| |
| // This is all a bit jury-rigged together. This is for running the app |
| // interactively; Selenium doesn't seem to handle the transition to https. |
| int port = secure ? 8443 : 9090; |
| |
| return String.format("%s://localhost:%d", protocol, port); |
| } |
| }; |
| |
| configuration.add(BaseURLSource.class, source); |
| } |
| |
| @Marker(Local.class) |
| public RequestFilter buildTimingFilter(final Logger log) |
| { |
| return new RequestFilter() |
| { |
| public boolean service(Request request, Response response, RequestHandler handler) throws IOException |
| { |
| long startTime = System.nanoTime(); |
| |
| try |
| { |
| return handler.service(request, response); |
| } finally |
| { |
| long elapsed = System.nanoTime() - startTime; |
| |
| log.info(String.format("Request time: %5.2f s -- %s", elapsed * 10E-10d, request.getPath())); |
| } |
| } |
| }; |
| } |
| |
| public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration, |
| |
| @Local |
| RequestFilter filter) |
| { |
| configuration.add("Timing", filter); |
| } |
| |
| public UserAuthenticator buildUserAuthenticator() |
| { |
| return new UserAuthenticator() |
| { |
| public boolean isValid(String userName, String plaintextPassword) |
| { |
| return plaintextPassword.equals("tapestry"); |
| } |
| }; |
| } |
| |
| public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration) |
| { |
| configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en,fr,de"); |
| configuration.add(SymbolConstants.PRODUCTION_MODE, false); |
| configuration.add(SymbolConstants.COMPRESS_WHITESPACE, false); |
| configuration.add(SymbolConstants.COMBINE_SCRIPTS, false); |
| |
| configuration.add(SymbolConstants.SECURE_ENABLED, true); |
| |
| configuration.add("app.injected-symbol", "Symbol contributed to ApplicationDefaults"); |
| |
| configuration.add(SymbolConstants.HMAC_PASSPHRASE, "testing, testing, 1... 2... 3..."); |
| |
| // This is the emphasis of testing at this point. |
| configuration.add(SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER, "jquery"); |
| |
| configuration.add(D3_URL_SYMBOL, "cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.js"); |
| configuration.add(SymbolConstants.PRELOADER_MODE, PreloaderMode.ALWAYS); |
| // configuration.add(SymbolConstants.ERROR_CSS_CLASS, "yyyy"); |
| // configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "classpath:/org/apache/tapestry5/integration/app1/app1.css"); |
| } |
| |
| public static void contributeIgnoredPathsFilter(Configuration<String> configuration) |
| { |
| configuration.add("/unreachable"); |
| } |
| |
| public ToDoDatabase buildToDoDatabase() |
| { |
| return new ToDoDatabaseImpl(); |
| } |
| |
| public MusicLibrary buildMusicLibrary(Logger log) |
| { |
| URL library = getClass().getResource("iTunes.xml"); |
| |
| final List<Track> tracks = new MusicLibraryParser(log).parseTracks(library); |
| |
| final Map<Long, Track> idToTrack = CollectionFactory.newMap(); |
| |
| for (Track t : tracks) |
| { |
| idToTrack.put(t.getId(), t); |
| } |
| |
| return new MusicLibrary() |
| { |
| public Track getById(long id) |
| { |
| Track result = idToTrack.get(id); |
| |
| if (result != null) |
| return result; |
| |
| throw new IllegalArgumentException(String.format("No track with id #%d.", id)); |
| } |
| |
| public List<Track> getTracks() |
| { |
| return tracks; |
| } |
| |
| public List<Track> findByMatchingTitle(String title) |
| { |
| String titleLower = title.toLowerCase(); |
| |
| List<Track> result = CollectionFactory.newList(); |
| |
| for (Track t : tracks) |
| { |
| if (t.getTitle().toLowerCase().contains(titleLower)) |
| result.add(t); |
| } |
| |
| return result; |
| } |
| }; |
| } |
| |
| @Marker(French.class) |
| public Greeter buildFrenchGreeter() |
| { |
| return new Greeter() |
| { |
| public String getGreeting() |
| |
| { |
| return "Bonjour!"; |
| } |
| }; |
| } |
| |
| public Greeter buildDefaultGreeter() |
| { |
| return new Greeter() |
| { |
| public String getGreeting() |
| |
| { |
| return "Hello"; |
| } |
| }; |
| } |
| |
| public static void contributeValueEncoderSource(MappedConfiguration<Class, ValueEncoderFactory> configuration, |
| final MusicLibrary library, final ToDoDatabase todoDatabase) |
| { |
| ValueEncoder<Track> trackEncoder = new ValueEncoder<Track>() |
| { |
| public String toClient(Track value) |
| { |
| return Long.toString(value.getId()); |
| } |
| |
| public Track toValue(String clientValue) |
| { |
| long id = Long.parseLong(clientValue); |
| |
| return library.getById(id); |
| } |
| }; |
| |
| configuration.add(Track.class, GenericValueEncoderFactory.create(trackEncoder)); |
| |
| ValueEncoder<ToDoItem> todoEncoder = new ValueEncoder<ToDoItem>() |
| { |
| public String toClient(ToDoItem value) |
| { |
| return String.valueOf(value.getId()); |
| } |
| |
| public ToDoItem toValue(String clientValue) |
| { |
| long id = Long.parseLong(clientValue); |
| |
| return todoDatabase.get(id); |
| } |
| }; |
| |
| configuration.add(ToDoItem.class, GenericValueEncoderFactory.create(todoEncoder)); |
| |
| final ValueEncoder<Entity> encoder = new ValueEncoder<Entity>() |
| { |
| public String toClient(Entity value) |
| { |
| return value.getId(); |
| } |
| |
| public Entity toValue(String clientValue) |
| { |
| Entity entity = new Entity(); |
| entity.setId(clientValue); |
| entity.setLabel("label" + clientValue); |
| return entity; |
| } |
| }; |
| |
| ValueEncoderFactory<Entity> valueEncoderFactory = new ValueEncoderFactory<Entity>() |
| { |
| |
| public ValueEncoder<Entity> create(Class<Entity> type) |
| { |
| return encoder; |
| } |
| }; |
| |
| configuration.add(Entity.class, valueEncoderFactory); |
| } |
| |
| public static void contributeComponentClassTransformWorker( |
| OrderedConfiguration<ComponentClassTransformWorker2> configuration) |
| { |
| configuration.add("ReverseStringsWorker", new ReverseStringsWorker()); |
| } |
| |
| public static void contributeComponentMessagesSource(@Value("context:WEB-INF/pre-app.properties") |
| Resource preappResource, OrderedConfiguration<Resource> configuration) |
| { |
| configuration.add("PreApp", preappResource, "before:AppCatalog"); |
| } |
| |
| // Remove this in 5.5: |
| @Contribute(ResourceDigestGenerator.class) |
| public static void protectPropertiesFiles(Configuration<String> configuration) |
| { |
| configuration.add("properties"); |
| } |
| |
| public void contributeValueLabelProvider(MappedConfiguration<Class, ValueLabelProvider> configuration) |
| { |
| configuration.add(Track.class, new ValueLabelProvider<Track>() |
| { |
| |
| public String getLabel(Track value) |
| { |
| return value.getTitle(); |
| } |
| }); |
| } |
| |
| @Contribute(ComponentClassResolver.class) |
| public static void setupAlphaLibrary(Configuration<LibraryMapping> configuration) |
| { |
| configuration.add(new LibraryMapping("lib/alpha", "org.apache.tapestry5.integration.locallib.alpha")); |
| } |
| |
| @Contribute(ClientWhitelist.class) |
| public static void provideWhitelistAnalyzer(OrderedConfiguration<WhitelistAnalyzer> configuration) |
| { |
| configuration.add("TestAnalyzer", new WhitelistAnalyzer() |
| { |
| |
| public boolean isRequestOnWhitelist(Request request) |
| { |
| return true; |
| } |
| }, "before:*"); |
| } |
| |
| @Contribute(PagePreloader.class) |
| public static void setupPagePreload(Configuration<String> configuration) |
| { |
| configuration.add("index"); |
| configuration.add("core/exceptionreport"); |
| configuration.add("core/t5dashboard"); |
| } |
| |
| |
| public static void contributeDefaultDataTypeAnalyzer( |
| @SuppressWarnings("rawtypes") MappedConfiguration<Class, String> configuration) |
| { |
| configuration.add(Address.class, "address"); |
| } |
| |
| @Contribute(BeanBlockSource.class) |
| public static void provideDefaultBeanBlocks(Configuration<BeanBlockContribution> configuration) |
| { |
| configuration.add( new EditBlockContribution("address", "PropertyEditBlocks", "object")); |
| } |
| |
| } |